Files
zeling_v2/Assets/Scripts/Enemies/EnemyBase.cs
2026-05-08 11:04:00 +08:00

165 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using Animancer;
using BaseGames.Combat;
namespace BaseGames.Enemies
{
/// <summary>
/// 敌人基类(架构 07_EnemyModule §1
/// 实现 IDamageable为 Behavior Designer 任务提供统一虚方法接口。
/// Phase 1 实现完整骨架BD 接口、受击、死亡流程。
/// ⚠️ _nav 字段类型为 IPathAgent在 BaseGames.Enemies.Navigation 中实现具体类)。
/// </summary>
public class EnemyBase : MonoBehaviour, IDamageable
{
[Header("配置 SO")]
[SerializeField] protected EnemyStatsSO _statsSO;
[SerializeField] protected EnemyAnimationConfigSO _animConfig;
[Header("子组件Prefab Inspector 绑定)")]
[SerializeField] protected EnemyStats _stats;
[SerializeField] protected EnemyMovement _movement;
[SerializeField] protected EnemyCombat _combat;
[SerializeField] protected AnimancerComponent _animancer;
[SerializeField] protected EnemyFeedback _feedback;
[SerializeField] protected HurtBox _hurtBox;
[Header("事件频道")]
[SerializeField] private BaseGames.Core.Events.TransformEventChannelSO _onEnemyDied;
// ── 导航代理IPathAgent由 EnemyNavAgent 实现)───────────────────
// Phase 1通过接口引用避免对 Navigation 程序集的直接依赖。
// 由子类 / Inspector 注入,或者运行时 GetComponent<IPathAgent>() 获取。
protected IPathAgent _nav;
// ── 状态 ──────────────────────────────────────────────────────────
private EnemyStateType _currentState;
public EnemyStateType CurrentState => _currentState;
// ── IDamageable ───────────────────────────────────────────────────
public bool IsInvincible => _currentState == EnemyStateType.Dead;
public int Defense => _stats != null ? _stats.Defense : 0;
public void TakeDamage(DamageInfo info)
{
if (_currentState == EnemyStateType.Dead) return;
_stats?.TakeDamage(info.FinalDamage);
_feedback?.OnHit(info);
if (_stats != null && _stats.CurrentHP <= 0)
{
Die();
return;
}
// Phase 2根据霸体结果选 Stagger / Hurt
ForceState(EnemyStateType.Hurt);
}
// ── BD 行为树接口(虚方法)────────────────────────────────────────
public virtual void MoveTo(Vector2 target)
=> _nav?.RequestMoveTo(target);
public virtual void MoveInDirection(float dir)
=> _movement?.MoveHorizontal(dir);
public virtual void StopMovement()
{
_nav?.StopNavigation();
_movement?.StopHorizontal();
}
public virtual void BeginAttack(AttackType type)
{
_combat?.StartAttack(type);
_stats?.ResetAttackCooldown();
}
public virtual bool CanAttack()
=> _stats != null && _stats.AttackCooldownTimer <= 0f;
public virtual bool IsPlayerInRange(float range)
=> _stats != null && _stats.DistanceToPlayer <= range;
public virtual void FacePlayer()
{
if (_playerTransform != null)
_movement?.FaceTarget(_playerTransform.position);
}
public virtual void Knockback(DamageInfo info)
{
if (info.Flags.HasFlag(DamageFlags.NoKnockback)) return;
_movement?.ApplyKnockback(info.KnockbackDirection, info.KnockbackForce);
}
// ── 状态控制 ──────────────────────────────────────────────────────
public void ForceState(EnemyStateType newState)
{
_currentState = newState;
// Phase 2根据状态播放对应动画 / 触发硬直计时
}
// ── Unity 生命周期 ────────────────────────────────────────────────
protected virtual void Awake()
{
_nav = GetComponent<IPathAgent>() ?? new NullPathAgent();
if (_stats != null && _statsSO != null)
_stats.Initialize(_statsSO);
// Phase 1简单查找玩家Phase 2 改为事件频道订阅
var playerGO = GameObject.FindWithTag("Player");
if (playerGO != null) _playerTransform = playerGO.transform;
}
protected virtual void Update()
{
_stats?.TickAttackCooldown(Time.deltaTime);
if (_playerTransform != null && _stats != null)
_stats.DistanceToPlayer = Vector2.Distance(transform.position, _playerTransform.position);
}
protected virtual void Start()
{
// 播放 Idle 动画(若 Animancer 和配置都就绪)
if (_animancer != null && _animConfig != null && _animConfig.Idle != null)
_animancer.Play(_animConfig.Idle);
}
// ── 内部 ──────────────────────────────────────────────────────────
private Transform _playerTransform;
protected virtual void Die()
{
if (_currentState == EnemyStateType.Dead) return;
ForceState(EnemyStateType.Dead);
// 禁用所有碰撞体
foreach (var col in GetComponentsInChildren<Collider2D>())
col.enabled = false;
// 播放死亡动画
if (_animancer != null && _animConfig != null && _animConfig.Dead != null)
{
var state = _animancer.Play(_animConfig.Dead);
state.Events(this).OnEnd = () => Destroy(gameObject);
}
else
{
Destroy(gameObject, 1.5f);
}
_feedback?.OnDeath();
_onEnemyDied?.Raise(transform);
}
}
// ── 枚举(架构 07 §1────────────────────────────────────────────────
public enum EnemyStateType { Controlled, Hurt, Stagger, Dead }
public enum AttackType { Melee, Ranged, Special }
}