refactor(scaffold): ChaoFeng + 通用 Place 统一 Box HurtBox/接触伤害区 + sprite 尺寸

This commit is contained in:
2026-07-20 16:35:25 +08:00
parent 4fcbc73d44
commit 3af4e4140b
@@ -271,9 +271,9 @@ namespace BaseGames.Editor
} }
[MenuItem("BaseGames/Scene/Place/Enemy (Basic)", priority = 110)] [MenuItem("BaseGames/Scene/Place/Enemy (Basic)", priority = 110)]
public static void PlaceEnemy() => PlaceEnemy(EnemyBodyColliderType.Box); public static void PlaceEnemy() => PlaceEnemy(EnemyBodyColliderType.Box, null);
public static void PlaceEnemy(EnemyBodyColliderType bodyCollider) public static void PlaceEnemy(EnemyBodyColliderType bodyCollider, Sprite defaultSprite = null)
{ {
var report = new List<string>(); var report = new List<string>();
int undoGroup = Undo.GetCurrentGroup(); int undoGroup = Undo.GetCurrentGroup();
@@ -291,12 +291,14 @@ namespace BaseGames.Editor
rb.constraints = RigidbodyConstraints2D.FreezeRotation; rb.constraints = RigidbodyConstraints2D.FreezeRotation;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous; rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
Collider2D body = CreateBodyCollider(go, bodyCollider, new Vector2(0.7f, 0.9f)); Vector2 size = SpriteSizeOr(defaultSprite, new Vector2(0.7f, 0.9f));
Collider2D body = CreateBodyCollider(go, bodyCollider, size);
Transform visual = GetOrCreateChild(go.transform, "Visual"); Transform visual = GetOrCreateChild(go.transform, "Visual");
visual.localPosition = (Vector3)(Vector2)body.offset; visual.localPosition = (Vector3)(Vector2)body.offset;
GetOrAddComponent<Animator>(visual.gameObject); GetOrAddComponent<Animator>(visual.gameObject);
AnimancerComponent animancer = GetOrAddComponent<AnimancerComponent>(visual.gameObject); AnimancerComponent animancer = GetOrAddComponent<AnimancerComponent>(visual.gameObject);
SpriteRenderer sr = SetupSpriteRenderer(visual.gameObject); SpriteRenderer sr = SetupSpriteRenderer(visual.gameObject);
if (defaultSprite != null) sr.sprite = defaultSprite;
EnemyBase enemyBase = GetOrAddComponent<EnemyBase>(go); EnemyBase enemyBase = GetOrAddComponent<EnemyBase>(go);
EnemyStats enemyStats = GetOrAddComponent<EnemyStats>(go); EnemyStats enemyStats = GetOrAddComponent<EnemyStats>(go);
@@ -307,21 +309,8 @@ namespace BaseGames.Editor
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent] GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go); PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
// HurtBox child var (hurtBox, bodyContact, contactHitBox) =
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox"); SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCollider = GetOrAddComponent<CapsuleCollider2D>(hurtBoxT.gameObject);
hurtCollider.isTrigger = true;
HurtBox hurtBox = GetOrAddComponent<HurtBox>(hurtBoxT.gameObject);
// Contact-damage HitBox child
Transform hitBodyT = GetOrCreateChild(go.transform, "HitBox_Body");
SetLayer(hitBodyT.gameObject, "EnemyHitBox", report);
CircleCollider2D hitCollider = GetOrAddComponent<CircleCollider2D>(hitBodyT.gameObject);
hitCollider.isTrigger = true;
hitCollider.radius = 0.55f;
HitBox hitBox = GetOrAddComponent<HitBox>(hitBodyT.gameObject);
GetOrAddComponent<BodyContactDamage>(hitBodyT.gameObject);
// Wire EnemyBase // Wire EnemyBase
AssignReference(enemyBase, "_stats", enemyStats, report); AssignReference(enemyBase, "_stats", enemyStats, report);
@@ -346,9 +335,9 @@ namespace BaseGames.Editor
// DamageSourceSO for body contact // DamageSourceSO for body contact
Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody", "DS_EnemyBody"); Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody", "DS_EnemyBody");
if (dmgSrc != null) if (dmgSrc != null)
AssignReference(hitBox, "_defaultSource", dmgSrc, report); AssignReference(contactHitBox, "_defaultSource", dmgSrc, report);
else else
report.Add("未找到 DamageSourceSOHitBox_Body._defaultSource 未绑定。请创建 CMB_DS_EnemyBody.asset。"); report.Add("未找到 DamageSourceSOContactDamageZone._defaultSource 未绑定。请创建 CMB_DS_EnemyBody.asset。");
SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "los" }, report); SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "los" }, report);
report.Add("★ 指定 EnemyBase._statsSO、_animConfig 资产(按所创建的敌人类型命名)。"); report.Add("★ 指定 EnemyBase._statsSO、_animConfig 资产(按所创建的敌人类型命名)。");
@@ -361,9 +350,9 @@ namespace BaseGames.Editor
} }
[MenuItem("BaseGames/Scene/Place/Boss Enemy", priority = 115)] [MenuItem("BaseGames/Scene/Place/Boss Enemy", priority = 115)]
public static void PlaceBossEnemy() => PlaceBossEnemy(EnemyBodyColliderType.Box); public static void PlaceBossEnemy() => PlaceBossEnemy(EnemyBodyColliderType.Box, null);
public static void PlaceBossEnemy(EnemyBodyColliderType bodyCollider) public static void PlaceBossEnemy(EnemyBodyColliderType bodyCollider, Sprite defaultSprite = null)
{ {
var report = new List<string>(); var report = new List<string>();
@@ -379,30 +368,18 @@ namespace BaseGames.Editor
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous; rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
rb.interpolation = RigidbodyInterpolation2D.Interpolate; rb.interpolation = RigidbodyInterpolation2D.Interpolate;
CreateBodyCollider(go, bodyCollider, new Vector2(1.5f, 2.5f)); Vector2 size = SpriteSizeOr(defaultSprite, new Vector2(1.5f, 2.5f));
CreateBodyCollider(go, bodyCollider, size);
GetOrAddComponent<Animator>(go); GetOrAddComponent<Animator>(go);
SetupSpriteRenderer(go); SpriteRenderer sr = SetupSpriteRenderer(go);
if (defaultSprite != null) sr.sprite = defaultSprite;
BossBase bossBase = GetOrAddComponent<BossBase>(go); BossBase bossBase = GetOrAddComponent<BossBase>(go);
EnemyStats bossStats = GetOrAddComponent<EnemyStats>(go); EnemyStats bossStats = GetOrAddComponent<EnemyStats>(go);
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go); PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
// HurtBox child var (hurtBox, bodyContact, contactHitBox) =
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox"); SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCollider = GetOrAddComponent<CapsuleCollider2D>(hurtBoxT.gameObject);
hurtCollider.isTrigger = true;
hurtCollider.size = new Vector2(1.5f, 2.5f);
HurtBox hurtBox = GetOrAddComponent<HurtBox>(hurtBoxT.gameObject);
// Contact-damage HitBox child
Transform hitBodyT = GetOrCreateChild(go.transform, "HitBox_Body");
SetLayer(hitBodyT.gameObject, "EnemyHitBox", report);
CircleCollider2D hitCollider = GetOrAddComponent<CircleCollider2D>(hitBodyT.gameObject);
hitCollider.isTrigger = true;
hitCollider.radius = 0.9f;
HitBox hitBox = GetOrAddComponent<HitBox>(hitBodyT.gameObject);
GetOrAddComponent<BodyContactDamage>(hitBodyT.gameObject);
// References // References
AssignReference(bossBase, "_stats", bossStats, report); AssignReference(bossBase, "_stats", bossStats, report);
@@ -410,9 +387,9 @@ namespace BaseGames.Editor
// DamageSourceSO // DamageSourceSO
Object dmgSrc = FindFirstAsset("CMB_DS_BossBody", "CMB_DS_EnemyBody", "DS_BossBody"); Object dmgSrc = FindFirstAsset("CMB_DS_BossBody", "CMB_DS_EnemyBody", "DS_BossBody");
if (dmgSrc != null) if (dmgSrc != null)
AssignReference(hitBox, "_defaultSource", dmgSrc, report); AssignReference(contactHitBox, "_defaultSource", dmgSrc, report);
else else
report.Add("未找到 DamageSourceSOHitBox_Body._defaultSource 未绑定。请按规范创建 CMB_DS_BossBody.asset。"); report.Add("未找到 DamageSourceSOContactDamageZone._defaultSource 未绑定。请按规范创建 CMB_DS_BossBody.asset。");
// Event channels // Event channels
AssignAsset(bossBase, "_onEnemyDied", report, false, "EVT_EnemyDied"); AssignAsset(bossBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
@@ -1063,9 +1040,9 @@ namespace BaseGames.Editor
} }
[MenuItem("BaseGames/Scene/Place/Boss 嘲风 (ChaoFeng)", priority = 117)] [MenuItem("BaseGames/Scene/Place/Boss 嘲风 (ChaoFeng)", priority = 117)]
public static void PlaceChaoFeng() => PlaceChaoFeng(EnemyBodyColliderType.Box); public static void PlaceChaoFeng() => PlaceChaoFeng(EnemyBodyColliderType.Box, null);
public static void PlaceChaoFeng(EnemyBodyColliderType bodyCollider) public static void PlaceChaoFeng(EnemyBodyColliderType bodyCollider, Sprite defaultSprite = null)
{ {
var report = new List<string>(); var report = new List<string>();
int undoGroup = Undo.GetCurrentGroup(); int undoGroup = Undo.GetCurrentGroup();
@@ -1083,12 +1060,14 @@ namespace BaseGames.Editor
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous; rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
rb.interpolation = RigidbodyInterpolation2D.Interpolate; rb.interpolation = RigidbodyInterpolation2D.Interpolate;
Collider2D body = CreateBodyCollider(go, bodyCollider, new Vector2(1.2f, 2.0f)); Vector2 size = SpriteSizeOr(defaultSprite, new Vector2(1.2f, 2.0f));
Collider2D body = CreateBodyCollider(go, bodyCollider, size);
Transform visual = GetOrCreateChild(go.transform, "Visual"); Transform visual = GetOrCreateChild(go.transform, "Visual");
visual.localPosition = (Vector3)(Vector2)body.offset; visual.localPosition = (Vector3)(Vector2)body.offset;
GetOrAddComponent<Animator>(visual.gameObject); GetOrAddComponent<Animator>(visual.gameObject);
AnimancerComponent animancer = GetOrAddComponent<AnimancerComponent>(visual.gameObject); AnimancerComponent animancer = GetOrAddComponent<AnimancerComponent>(visual.gameObject);
SpriteRenderer srBoss = SetupSpriteRenderer(visual.gameObject); SpriteRenderer srBoss = SetupSpriteRenderer(visual.gameObject);
if (defaultSprite != null) srBoss.sprite = defaultSprite;
ChaoFengBoss bossBase = GetOrAddComponent<ChaoFengBoss>(go); ChaoFengBoss bossBase = GetOrAddComponent<ChaoFengBoss>(go);
EnemyStats bossStats = GetOrAddComponent<EnemyStats>(go); EnemyStats bossStats = GetOrAddComponent<EnemyStats>(go);
@@ -1102,13 +1081,9 @@ namespace BaseGames.Editor
ChaoFengKnockdownCounter knockdown = GetOrAddComponent<ChaoFengKnockdownCounter>(go); ChaoFengKnockdownCounter knockdown = GetOrAddComponent<ChaoFengKnockdownCounter>(go);
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go); PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
// HurtBox // HurtBox + ContactDamageZoneBoss 身体接触伤害区,默认启用;策划可在 Inspector 关闭该子节点)
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox"); var (hurtBox, bodyContact, contactHitBox) =
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report); SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
CapsuleCollider2D hurtCap = GetOrAddComponent<CapsuleCollider2D>(hurtBoxT.gameObject);
hurtCap.isTrigger = true;
hurtCap.size = new Vector2(1.1f, 1.9f);
HurtBox hurtBox = GetOrAddComponent<HurtBox>(hurtBoxT.gameObject);
// Phase1 攻击 HitBox(默认禁用;技能执行时由 BossSkillExecutor 开关)。 // Phase1 攻击 HitBox(默认禁用;技能执行时由 BossSkillExecutor 开关)。
// 计划:挥扇三连 FanCombo ×3 + 龙卷接触 Tornado。 // 计划:挥扇三连 FanCombo ×3 + 龙卷接触 Tornado。
@@ -1191,7 +1166,7 @@ namespace BaseGames.Editor
Object dmgSrc = FindFirstAsset("CMB_DS_BossBody", "CMB_DS_EnemyBody"); Object dmgSrc = FindFirstAsset("CMB_DS_BossBody", "CMB_DS_EnemyBody");
if (dmgSrc != null) if (dmgSrc != null)
{ {
foreach (var hb in new[] { fan1, fan2, fan3, tornadoHB }) foreach (var hb in new[] { fan1, fan2, fan3, tornadoHB, contactHitBox })
if (hb != null) AssignReference(hb, "_defaultSource", dmgSrc, report); if (hb != null) AssignReference(hb, "_defaultSource", dmgSrc, report);
} }