feat(enemy): EnemyBase 持有身体几何权威(_bodyCollider + Body),Awake/OnValidate 双重校验

This commit is contained in:
2026-07-27 11:51:45 +08:00
parent b0e1ebfecc
commit 6026911532
+18
View File
@@ -43,6 +43,11 @@ namespace BaseGames.Enemies
[SerializeField] protected EnemyFeedback _feedback;
[SerializeField] protected HurtBox _hurtBox;
[Header("身体碰撞体(唯一权威源)")]
[Tooltip("用于身体几何(宽高/边缘/探测起点)的碰撞体。留空 = 使用本物体上的 Collider2D。" +
"移动探测、导航内缩、冲刺终点等全部以此为准,不要在各处自行取碰撞体。")]
[SerializeField] private Collider2D _bodyCollider;
[Header("区域(可选)")]
[Tooltip("地图固定巡逻/追击区域;配置后 BD_ChasePlayer 以区域边界替代 MaxChaseDistanceBD_ReturnToHome 归位至区域中心。留空则沿用出生点 + MaxChaseDistance 旧逻辑。")]
[SerializeField] private EnemyPatrolZone _patrolZone;
@@ -69,6 +74,9 @@ namespace BaseGames.Enemies
// 碰撞体缓存:Awake 时收集一次,避免 Die()/OnSpawn() 中频繁 GetComponentsInChildren 分配
private Collider2D[] _colliders;
// 身体几何权威(懒创建;OnValidate 改引用后置空重建)
private EnemyBody _body;
// ── 对象池支持 ─────────────────────────────────────────────────────
/// <summary>
/// 本 GameObject 上的 PooledObject 组件(可选)。
@@ -224,6 +232,8 @@ namespace BaseGames.Enemies
/// <summary>攻击选择器(从已注册能力里筛出 category==Attack 的候选,Awake 时构建)。</summary>
public Abilities.EnemyAttackSelector AttackSelector => _attackSelector;
private Abilities.EnemyAttackSelector _attackSelector;
/// <summary>身体碰撞体几何的唯一权威查询口。移动/导航/能力一律经此获取身体尺寸。</summary>
public IEnemyBody Body => _body ??= new EnemyBody(gameObject, _bodyCollider);
/// <summary>由 _onPlayerSpawned 事件缓存的玩家 Transform,供 BD 任务读取。</summary>
public Transform PlayerTransform => _playerTransform;
/// <summary>感知 Hub;供 BD 任务及 QuotaManager 暂停/恢复感知使用。</summary>
@@ -543,6 +553,11 @@ namespace BaseGames.Enemies
BuildAttackSelector();
_colliders = GetComponentsInChildren<Collider2D>(true);
// 身体几何权威校验:解析不到碰撞体 → 显式报错(根因暴露,不静默兜底)
if (Body.Collider == null)
Debug.LogError($"[EnemyBase] {name} 找不到身体碰撞体:请在 Inspector 指定 _bodyCollider" +
"或确保本物体上挂有 Collider2D。移动探测/导航内缩将不可用。", this);
// 收集配置型行为模块(零代码扩展点)
_deathSequence = GetComponentInChildren<Behaviors.IEnemyDeathSequence>(true);
GetComponentsInChildren(true, _spawnHandlers);
@@ -757,6 +772,7 @@ namespace BaseGames.Enemies
protected virtual void OnValidate()
{
_body = null; // 身体碰撞体引用可能在 Inspector 被改,缓存无条件失效(与警告抑制无关)
if (SuppressValidationWarnings) return;
if (_statsSO == null)
Debug.LogWarning($"[EnemyBase] {gameObject.name} 缺少 EnemyStatsSO 配置(运行时会 NullRef)。", this);
@@ -764,6 +780,8 @@ namespace BaseGames.Enemies
Debug.LogWarning($"[EnemyBase] {gameObject.name} 未绑定 EnemyStats 组件引用。", this);
if (_animancer == null)
Debug.LogWarning($"[EnemyBase] {gameObject.name} 未绑定 AnimancerComponent 引用。", this);
if (_bodyCollider == null && GetComponent<Collider2D>() == null)
Debug.LogWarning($"[EnemyBase] {gameObject.name} 找不到身体碰撞体(未指定 _bodyCollider 且本物体无 Collider2D)。", this);
}
#endif