Files
zeling_v2/Docs_Dev/superpowers/specs/2026-07-10-enemy-sprite-driven-box-colliders-design.md
T
2026-07-21 10:22:43 +08:00

117 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 敌人碰撞体:Sprite 驱动 + 统一 Box + 全员接触伤害区 —— 设计文档
- 日期:2026-07-10
- 范围:**只改脚手架 `SceneObjectPlacerTool` 与向导 `CharacterWizardWindow`**;不动现有 prefab(用户后续手动重生成替换)。
## 目标
脚手架生成的每个敌人(含 Boss)满足:
1. **主体碰撞体、HurtBox、ContactDamageZone 三者统一为 `BoxCollider2D`**,尺寸完全一致、**底部对齐 y=0**(敌人原点在碰撞体底部,参考 E001;供 PathBerserker2d NavAgent 检测原点落在导航线上)。
2. 三者尺寸由**向导指定的默认 Sprite 的包围盒**(`sprite.bounds.size`)推导;Sprite 留空则回退到各敌人现有的硬编码尺寸(不破坏无 sprite 的旧行为)。
3. **每个敌人 + Boss 都有 ContactDamageZone**(接触伤害区);不需要接触伤害的敌人由策划把该节点置为非激活。
## 背景(现状)
- `SceneObjectPlacerTool``CreateBodyCollider(go, type, size)` 已支持 Box/Capsule/Circle + `AlignColliderBottomToPivot`(底部对齐)。主体默认 Box。
- HurtBox 各敌人现为 `CapsuleCollider2D`E001 已是 Box);ContactDamageZone 参差:E001=Box、E003/E006=Circle、**E002/E004/E005/ChaoFeng 缺失**。
- 尺寸为每敌人硬编码(如 E001 body 0.6×0.8E002 0.5×0.7…)。
- `EnsureCollidersAreTriggers(go)` 助手已存在(把节点全部 Collider2D 设 trigger)。
- `CharacterWizardWindow` 小怪/ Boss Tab 已有"主体碰撞器类型"EnumField,调 `PlaceSpecificEnemy(id, colliderType)` / `PlaceChaoFeng`
## 设计
### A. 向导:默认 Sprite 字段(可选)
- 小怪 Tab、Boss Tab 各加一个 `UnityEditor.UIElements.ObjectField``objectType = typeof(Sprite)`),标签"默认外观 Sprite(碰撞体尺寸依据,可留空)",绑定 `Sprite _enemyDefaultSprite`Boss 用同一字段或独立 `_bossDefaultSprite`,实现上用一个 `_defaultSprite` 即可,按当前 Tab 复用)。
- 放置按钮把选中 Sprite 传入:`PlaceSpecificEnemy(id, colliderType, _defaultSprite)``PlaceChaoFeng(colliderType, _defaultSprite)`
### B. 脚手架:Sprite 驱动 + 统一 Box + 全员接触伤害区
**新增两个助手:**
```csharp
// Sprite 有则用其世界包围盒尺寸,否则回退。
private static Vector2 SpriteSizeOr(Sprite s, Vector2 fallback)
=> s != null ? (Vector2)s.bounds.size : fallback;
// 建 HurtBox + ContactDamageZone 两个子节点,均为 Box、同尺寸、底部对齐、isTrigger。
// contactEnabledBodyContactDamage 初始启用状态(不需要接触伤害的敌人可置 false 或后续置节点非激活)。
// 返回创建的 (hurtBox, bodyContact, contactHitBox) 供上层按需接线。
private static (HurtBox hurt, BodyContactDamage contact, HitBox contactHitBox)
SetupHurtAndContactBoxes(GameObject root, Vector2 size, bool contactEnabled, List<string> report)
{
// HurtBox 子节点
var hurtT = GetOrCreateChild(root.transform, "HurtBox");
SetLayer(hurtT.gameObject, "EnemyHurtBox", report);
var hurtBox2D = GetOrAddComponent<BoxCollider2D>(hurtT.gameObject);
hurtBox2D.size = size; hurtBox2D.isTrigger = true;
AlignColliderBottomToPivot(hurtBox2D);
var hurtBox = GetOrAddComponent<HurtBox>(hurtT.gameObject);
EnsureCollidersAreTriggers(hurtT.gameObject);
// ContactDamageZone 子节点
var contactT = GetOrCreateChild(root.transform, "ContactDamageZone");
SetLayer(contactT.gameObject, "EnemyHitBox", report);
var contactBox = GetOrAddComponent<BoxCollider2D>(contactT.gameObject);
contactBox.size = size; contactBox.isTrigger = true;
AlignColliderBottomToPivot(contactBox);
var contactHitBox = GetOrAddComponent<HitBox>(contactT.gameObject);
var bodyContact = GetOrAddComponent<BodyContactDamage>(contactT.gameObject);
bodyContact.enabled = contactEnabled;
EnsureCollidersAreTriggers(contactT.gameObject);
return (hurtBox, bodyContact, contactHitBox);
}
```
**每个 `PlaceExxx` 签名加可选 sprite;用它推导 size、赋给 SpriteRenderer、走统一助手:**
```csharp
public static void PlaceE0xx(EnemyBodyColliderType bodyCollider = EnemyBodyColliderType.Box,
Sprite defaultSprite = null)
{
...
Vector2 size = SpriteSizeOr(defaultSprite, /*该敌人原硬编码 fallback*/);
var sr = SetupSpriteRenderer(visual.gameObject);
if (defaultSprite != null) sr.sprite = defaultSprite; // 视觉与碰撞体一致
CreateBodyCollider(go, bodyCollider, size); // 主体 Box(size)、底部对齐
var (hurtBox, bodyContact, contactHitBox) =
SetupHurtAndContactBoxes(go, size, contactEnabled: true, report);
// 该敌人特有接线(如 E001contactEnabled=false + 把 bodyContact 绑给 ContactChaseAbility._contactDamage
// contactHitBox 绑 CMB_DS_EnemyBody 伤害源)。
...
}
```
- **主体默认 Box**`EnemyBodyColliderType.Box`);Capsule/Circle 选项保留但默认 Box。三者尺寸都 = `size`
- **接触伤害默认启用**(碰到即伤):`contactEnabled: true`**E001 例外** `contactEnabled: false`(其 ContactChaseAbility 在冲刺期开启接触伤害),并把 `bodyContact` 绑到 `ContactChaseAbility._contactDamage`
- ContactDamageZone 的 HitBox 绑默认伤害源 `CMB_DS_EnemyBody`(沿用 E001 现有做法)。
- Boss `PlaceChaoFeng` 同样通过助手补 ContactDamageZone(默认启用,策划按需置非激活)。
- 全部走 `AlignColliderBottomToPivot`(底部对齐,中心在底部)。
### C. 不动现有 prefab
现有 7 个 prefab 不由本次修改,用户用更新后的脚手架手动重生成替换。
## 影响文件
- `Assets/_Game/Scripts/Editor/Scene/SceneObjectPlacerTool.cs`:新增 `SpriteSizeOr` / `SetupHurtAndContactBoxes`;改 7 个 `PlaceExxx`(E001-E006) + `PlaceChaoFeng` 用统一助手 + sprite 参数;替换原 Capsule/Circle 的 HurtBox/ContactDamageZone 创建;给缺失的 4 个敌人补 ContactDamageZone。
- `Assets/_Game/Scripts/Editor/Character/CharacterWizardWindow.cs`:加 `_defaultSprite` 字段 + ObjectField;放置调用传 sprite。
## 验证
- 编译 0 错误。
- 反射/直接调用各 `PlaceExxx` 生成到临时场景对象,校验:
- 主体/HurtBox/ContactDamageZone 都是 `BoxCollider2D`
- HurtBox/ContactDamageZone `isTrigger=true`,主体 `isTrigger=false`
- 三者 `size` 一致(= sprite 包围盒 或 fallback)。
- 三者底部对齐(`offset.y == size.y * 0.5`)。
- 每个敌人 + Boss 均存在 ContactDamageZoneBodyContactDamage 组件在)。
- E001BodyContactDamage 初始 disabled 且已绑到 ContactChaseAbility。
- 用一张测试 Sprite 走一次向导放置,确认碰撞体尺寸 = sprite 包围盒、SpriteRenderer 用了该 sprite。
## 非目标
- 不改现有 prefab(用户手动重生成)。
- 不改敌人 AI / 移动 / 伤害逻辑。
- 不引入 sprite 紧密网格/物理形状(用完整包围盒即可)。