165 lines
6.7 KiB
C#
165 lines
6.7 KiB
C#
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 }
|
||
}
|