refactor(enemy): 身体接触伤害改为常驻开启并与能力解耦

所有敌人默认常驻身体接触伤害:存活即开、死亡时经 PerformDeath 统一关碰撞体停止。
- ContactChaseAbility/AnimatedCeilingDropAbility 移除对 BodyContactDamage 的开关引用
- BodyContactDamage 语义改为常驻(更新注释)
- 脚手架 SetupHurtAndContactBoxes 去掉 contactEnabled 参数恒为 true,并把
  ContactDamageZone 的 HitBox._targetLayers 限定为 PlayerHurtBox(避免常驻后敌人互相误伤)
- 7 个敌人预制体:E001 启用 BodyContactDamage;E002/E004/E005/ChaoFeng 补齐
  ContactDamageZone(镜像 HurtBox 身体碰撞体);全部 _targetLayers 收敛为 PlayerHurtBox
- 更新 Docs/Guides/02 敌人搭建指南

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-21 16:18:07 +08:00
co-authored by Claude Opus 4.8
parent b4379db5de
commit 6f519ec9a9
12 changed files with 461 additions and 225 deletions
@@ -310,7 +310,7 @@ namespace BaseGames.Editor
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetupHurtAndContactBoxes(go, size, report);
// Wire EnemyBase
AssignReference(enemyBase, "_stats", enemyStats, report);
@@ -379,7 +379,7 @@ namespace BaseGames.Editor
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetupHurtAndContactBoxes(go, size, report);
// References
AssignReference(bossBase, "_stats", bossStats, report);
@@ -450,7 +450,7 @@ namespace BaseGames.Editor
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: false, report); // E001 伪装期不伤,追击能力
SetupHurtAndContactBoxes(go, size, report); // 身体接触伤害常驻开启(与追击能力解耦)
// Model AAI 只决策触发哪个能力(Idle/Patrol/Alert 已改为纯移动意图,由 EnemyLocomotion 执行,无需挂能力组件)
Transform abilitiesT = GetOrCreateChild(go.transform, "Abilities");
@@ -484,7 +484,6 @@ namespace BaseGames.Editor
AssignLayerMask(movement, "_wallMask", new[] { "Platform", "Wall" }, report); // 撞墙判定层(策划可调)
AssignAsset(chaseAbility, "_config", report, false, "ABL_E001_Chase");
AssignReference(chaseAbility, "_contactDamage", bodyContact, report);
// 追击动画:起手/循环/收招(设计文档 Skill_Start/Loop/End
AssignClipTransition(chaseAbility, "_startClip", "Skill_Start", "Enemies/E001", report);
AssignClipTransition(chaseAbility, "_loopClip", "Skill_Loop", "Enemies/E001", report);
@@ -545,9 +544,9 @@ namespace BaseGames.Editor
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
// HurtBox + ContactDamageZonecomponent.enabled 初始为 false:仅悬挂脆弱窗口期间由能力开启
// HurtBox + ContactDamageZone身体接触伤害常驻开启;HurtBox 悬挂期外关闭见下
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetupHurtAndContactBoxes(go, size, report);
hurtBox.enabled = false; // 悬挂阶段外不可被攻击;CeilingHangStrikeAbility 在 _loopClip 期间开启
// AttackHitBox(钻出啃咬瞬间判定,由 CeilingHangStrikeAbility 激活)
@@ -647,7 +646,7 @@ namespace BaseGames.Editor
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetupHurtAndContactBoxes(go, size, report);
Transform abilitiesT = GetOrCreateChild(go.transform, "Abilities");
Transform fallT = GetOrCreateChild(abilitiesT, "AnimatedCeilingDropAbility");
@@ -678,7 +677,6 @@ namespace BaseGames.Editor
AssignLayerMask(movement, "_wallMask", new[] { "Platform", "Wall" }, report); // 撞墙判定层(策划可调)
AssignAsset(fallAbility, "_config", report, false, "ABL_E003_Fall");
AssignReference(fallAbility, "_contactDamage", bodyContact, report);
// 出生 / 外部触发执行下坠能力(零代码:替代过去的 E003_YouZhi 专属脚本)
EnemyAbilityTrigger fallTrigger = GetOrAddComponent<EnemyAbilityTrigger>(go);
@@ -737,7 +735,7 @@ namespace BaseGames.Editor
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetupHurtAndContactBoxes(go, size, report);
Transform biteT = GetOrCreateChild(go.transform, "BiteHitBox");
SetLayer(biteT.gameObject, "EnemyHitBox", report);
@@ -866,7 +864,7 @@ namespace BaseGames.Editor
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetupHurtAndContactBoxes(go, size, report);
Transform biteT = GetOrCreateChild(go.transform, "BiteHitBox");
SetLayer(biteT.gameObject, "EnemyHitBox", report);
@@ -977,7 +975,7 @@ namespace BaseGames.Editor
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetupHurtAndContactBoxes(go, size, report);
Transform landT = GetOrCreateChild(go.transform, "LandingHitBox");
SetLayer(landT.gameObject, "EnemyHitBox", report);
@@ -1021,7 +1019,6 @@ namespace BaseGames.Editor
AssignAsset(chaseAbl, "_config", report, false, "ABL_E006_Chase");
AssignReference(leapAbl, "_landingHitBox", landHitBox, report);
AssignReference(chaseAbl, "_contactDamage", bodyContact, report);
Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody");
if (dmgSrc != null)
@@ -1083,7 +1080,7 @@ namespace BaseGames.Editor
// HurtBox + ContactDamageZoneBoss 身体接触伤害区,默认启用;策划可在 Inspector 关闭该子节点)
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetupHurtAndContactBoxes(go, size, report);
// Phase1 攻击 HitBox(默认禁用;技能执行时由 BossSkillExecutor 开关)。
// 计划:挥扇三连 FanCombo ×3 + 龙卷接触 Tornado。
@@ -2253,11 +2250,12 @@ namespace BaseGames.Editor
/// <summary>
/// 建/更新 HurtBox 与 ContactDamageZone 两个子节点:均为 BoxCollider2D、同尺寸 size、
/// 底部对齐 y=0、isTrigger=true。ContactDamageZone 带 HitBox + BodyContactDamage。
/// contactEnabledBodyContactDamage 初始启用状态(不需要接触伤害的敌人可传 false 或后续置节点非激活)。
/// 返回 (hurtBox, bodyContact, contactHitBox) 供上层按需接线(如伤害源/能力引用)
/// 身体接触伤害默认常驻开启:所有敌人存活期间身体接触即持续造成伤害,与能力解耦、不由能力开关,
/// 死亡时经 EnemyBase.PerformDeath 统一禁用碰撞体而停止
/// 返回 (hurtBox, bodyContact, contactHitBox) 供上层按需接线(如伤害源)。
/// </summary>
private static (HurtBox hurt, BodyContactDamage contact, HitBox contactHitBox)
SetupHurtAndContactBoxes(GameObject root, Vector2 size, bool contactEnabled, List<string> report)
SetupHurtAndContactBoxes(GameObject root, Vector2 size, List<string> report)
{
// HurtBox 子节点
var hurtT = GetOrCreateChild(root.transform, "HurtBox");
@@ -2277,8 +2275,10 @@ namespace BaseGames.Editor
contactCol.isTrigger = true;
AlignColliderBottomToPivot(contactCol);
var contactHitBox = GetOrAddComponent<HitBox>(contactT.gameObject);
// 身体接触伤害只命中玩家,避免常驻开启后敌人之间互相误伤(EnemyHitBox × EnemyHurtBox 矩阵默认不忽略)
AssignLayerMask(contactHitBox, "_targetLayers", new[] { "PlayerHurtBox" }, report);
var bodyContact = GetOrAddComponent<BodyContactDamage>(contactT.gameObject);
bodyContact.enabled = contactEnabled;
bodyContact.enabled = true; // 常驻开启:所有敌人默认身体接触伤害,存活即开、死亡才关
EnsureCollidersAreTriggers(contactT.gameObject);
return (hurtBox, bodyContact, contactHitBox);