using UnityEngine; using Animancer; using BaseGames.Combat; using BaseGames.Core.Events; using BaseGames.Enemies.States; #if GRAPH_DESIGNER using Opsive.BehaviorDesigner.Runtime; #endif namespace BaseGames.Enemies { /// /// 敌人基类(架构 07_EnemyModule §1)。 /// 实现 IDamageable,为 Behavior Designer 任务提供统一虚方法接口。 /// 包含:BD 接口、受击、死亡流程。 /// ⚠️ _nav 字段类型为 IPathAgent(在 BaseGames.Enemies.Navigation 中实现具体类)。 /// public class EnemyBase : MonoBehaviour, IDamageable, ILOSRequester { [Header("标识")] [SerializeField] private string _enemyId; // 任务系统 / Boss 进程追踪用,如 "Enemy_SpiderGuard" public string EnemyId => _enemyId; /// 死亡时触发(ChallengeRoomManager 波次结算用)。 public event System.Action OnDied; [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.StringEventChannelSO _onEnemyDied; /// /// 玩家生成事件频道(由 PlayerController.Start() 广播)。 /// 配置后替代 FindWithTag,避免 N 个敌人同帧全场景标签扫描。 /// [SerializeField] private BaseGames.Core.Events.TransformEventChannelSO _onPlayerSpawned; // ── 导航代理(IPathAgent;由 EnemyNavAgent 实现)─────────────────── // 通过接口引用,避免对 Navigation 程序集的直接依赖。 // 由子类 / Inspector 注入,或者运行时 GetComponent() 获取。 protected IPathAgent _nav; // 霸体来源(由 EnemyPoiseComponent.Awake() 自动注入,TakeDamage 时读取) private IPoiseSource _poiseSource; private readonly CompositeDisposable _subs = new(); // ── 状态 ────────────────────────────────────────────────────────── private EnemyStateType _currentState; public EnemyStateType CurrentState => _currentState; // POCO 状态对象字典:枚举保持对外 API 不变。 // 子类可在 Awake() 重写条目注入自定义状态对象。 protected readonly System.Collections.Generic.Dictionary _stateObjs = new System.Collections.Generic.Dictionary(); // ── 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; } // 根据霸体等级选择 Stagger(硬直)或 Hurt(受击)。 // ForceBreak 标记或 BreakLevel 超过当前霸体等级时触发 Stagger,否则触发 Hurt。 PoiseLevel curPoise = _poiseSource?.GetCurrentPoiseLevel() ?? PoiseLevel.None; bool causesStagger = info.Flags.HasFlag(DamageFlags.ForceBreak) || (int)info.Break > (int)curPoise; ForceState(causesStagger ? EnemyStateType.Stagger : EnemyStateType.Hurt); } // BD 任务访问接口(公共只读属性)──────────────────────────────── public IPathAgent Nav => _nav; public EnemyMovement Movement => _movement; public EnemyStats Stats => _stats; public AnimancerComponent Animancer => _animancer; public EnemyAnimationConfigSO AnimConfig => _animConfig; /// 由 _onPlayerSpawned 事件缓存的玩家 Transform,供 BD 任务读取。 public Transform PlayerTransform => _playerTransform; #if GRAPH_DESIGNER public BehaviorTree BehaviorTree => _behaviorTree; #endif // ── 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.SqrDistanceToPlayer <= range * range; public virtual bool IsPlayerVisible() => _losResult; // BatchLOSSystem 写入;初始 false(未见玩家) 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 virtual void JumpTo(Vector2 target) => _movement?.JumpToTarget(target); /// /// 调整 BehaviorTree Tick 频率(非警觉=2帧/次,警觉=每帧)。 /// 由 BD_SetAlert 调用(架构 07_EnemyModule §13.5)。 /// public void SetAggroTickRate(bool isAggro) { #if GRAPH_DESIGNER // Opsive 运行时当前版本未直接暴露 frameInterval 字段。 // 需升级 Opsive 包或通过自定义 Tick 次数属性实现此功能。 Debug.LogWarning("[EnemyBase] SetAggroTickRate 当前无效:Opsive 运行时尚未暴露 frameInterval,请升级包后实现。", this); _ = isAggro; #endif } // ── 动画事件钩子(由 EnemyAnimationEvents 调用)──────────────────── /// 生成弹幕 / 技能投射物。payload 为配置 Id,由子类查表实现。 public virtual void SpawnProjectile(string payload) { } /// 切换二阶段形态(Boss 等特殊敌人重写此方法)。 public virtual void TriggerPhaseTwo() { } /// 动画播放完毕回调(用于单次动画后返回 Idle 等逻辑)。 public virtual void OnAnimationComplete(string payload) { } /// 设置嘶吼状态(影响 Blackboard / 状态机行为)。 public virtual void SetRoaring(bool isRoaring) { } // ── 状态控制 ────────────────────────────────────────────────────── public void ForceState(EnemyStateType newState) { // Exit 当前状态 if (_stateObjs.TryGetValue(_currentState, out var prev)) prev.Exit(this); _currentState = newState; // Enter 新状态 if (_stateObjs.TryGetValue(newState, out var next)) next.Enter(this); } // ── Unity 生命周期 ──────────────────────────────────────────────── protected virtual void Awake() { // 初始化 POCO 状态对象(子类可在调用 base.Awake() 后替换字典条目) _stateObjs[EnemyStateType.Controlled] = new EnemyControlledState(); _stateObjs[EnemyStateType.Hurt] = new EnemyHurtState(); _stateObjs[EnemyStateType.Stagger] = new EnemyStaggerState(); _stateObjs[EnemyStateType.Dead] = new EnemyDeadState(); _nav = GetComponent() ?? new NullPathAgent(); _poiseSource = GetComponent(); Debug.Assert(_statsSO != null, "[EnemyBase] _statsSO 未赋值,请在 Prefab Inspector 中指定 EnemyStatsSO。", this); Debug.Assert(_stats != null, "[EnemyBase] _stats 未绑定,请在 Prefab Inspector 中绑定 EnemyStats 组件。", this); _stats.Initialize(_statsSO); // 订阅玩家生成事件(PlayerController.Start 广播),避免每个敌人独立 FindWithTag // 订阅在 OnEnable 中处理 #if GRAPH_DESIGNER _behaviorTree = GetComponent(); if (_behaviorTree != null) { _behaviorTree.StartWhenEnabled = false; _behaviorTree.PauseWhenDisabled = true; _behaviorTree.StartBehavior(); } #endif } protected virtual void Update() { _stats?.TickAttackCooldown(Time.deltaTime); // 使用 sqrMagnitude 替代 Vector2.Distance,避免每帧开平方计算 if (_playerTransform != null && _stats != null) _stats.SqrDistanceToPlayer = ((Vector2)_playerTransform.position - (Vector2)transform.position).sqrMagnitude; } protected virtual void Start() { // 若事件未配置或玩家尚未广播,匹降为一次性查找 if (_playerTransform == null) { var playerGO = GameObject.FindWithTag("Player"); if (playerGO != null) _playerTransform = playerGO.transform; } // 播放 Idle 动画(若 Animancer 和配置都就绪) if (_animancer != null && _animConfig != null && _animConfig.Idle != null) _animancer.Play(_animConfig.Idle); } // ── 内部 ────────────────────────────────────────────────────────── protected Transform _playerTransform; private void SetPlayerTransform(Transform player) => _playerTransform = player; protected virtual void OnEnable() { _onPlayerSpawned?.Subscribe(SetPlayerTransform).AddTo(_subs); } protected virtual void OnDisable() { _subs.Clear(); } protected virtual void OnDestroy() { } // LOS 缓存(BatchLOSSystem 写入;降级时由 3 帧节流 Raycast 写入) private bool _losResult; // ── ILOSRequester ────────────────────────────────────────────────── public Vector2 LOSOrigin => (Vector2)transform.position + _statsSO.EyeOffset; public Vector2 LOSTarget => _playerTransform != null ? (Vector2)_playerTransform.position : (Vector2)transform.position; public LayerMask LOSBlockingMask => _statsSO.LOSBlockingMask; public void ReceiveLOSResult(bool hasLineOfSight) { _losResult = hasLineOfSight; } // BehaviorTree 引用(#if GRAPH_DESIGNER 保护,避免空引用) #if GRAPH_DESIGNER private BehaviorTree _behaviorTree; #endif protected virtual void Die() { if (_currentState == EnemyStateType.Dead) return; ForceState(EnemyStateType.Dead); // 禁用所有碰撞体 foreach (var col in GetComponentsInChildren()) 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(_enemyId); OnDied?.Invoke(); } } // ── 枚举(架构 07 §1)──────────────────────────────────────────────── public enum EnemyStateType { Controlled, Hurt, Stagger, Dead } public enum AttackType { Melee, Ranged, Special } }