10 KiB
Unity Layer 命名与用途规范
版本:1.0
创建日期:2026-05
适用范围:项目所有 GameObject 的 Layer 分配
关联文档:Standards/AssetFolderSpec.md、Editor/Tools/Physics2DLayerReport.cs
目录
1. 概述
本项目使用固定的 Layer 名称集合来驱动 Physics 2D 碰撞矩阵、伤害判定、视线检测(LOS)、地面检测等核心系统。所有 Layer 必须在 Edit → Project Settings → Tags and Layers 中手动创建,名称必须与本文档一致(大小写敏感)。
重要:代码中通过
LayerMask.NameToLayer("LayerName")引用 Layer,若 Layer 不存在将返回-1并导致逻辑错误。创建场景对象时请优先使用BaseGames → Scene → Place菜单工具,该工具会自动校验并赋值 Layer。
2. Layer 清单
2.1 玩家相关
| Layer 名称 | 挂载对象 | 用途说明 |
|---|---|---|
Player |
玩家根节点 GameObject | 玩家物理碰撞体(含 Rigidbody2D);地面检测、存档点、检查点触发器通过此 Layer 筛选玩家 |
PlayerHurtBox |
玩家子节点 HurtBox |
接收伤害的碰撞体(isTrigger);EnemyHitBox / EnemyProjectile 命中此 Layer 时对玩家造成伤害 |
PlayerHitBox |
玩家武器 / 技能 HitBox | 发出伤害的碰撞体;命中 EnemyHurtBox 时触发对敌人的伤害判定 |
PlayerProjectile |
玩家发射的投射物 | 玩家子弹;不与 PlayerHurtBox、EnemyProjectile 碰撞,命中 EnemyHurtBox 或 Ground 时有效 |
2.2 敌人相关
| Layer 名称 | 挂载对象 | 用途说明 |
|---|---|---|
Enemy |
敌人根节点 GameObject | 敌人物理碰撞体(含 Rigidbody2D);与 Ground 碰撞实现站立 |
EnemyHurtBox |
敌人子节点 HurtBox |
接收伤害的碰撞体(isTrigger);PlayerHitBox / PlayerProjectile 命中此 Layer 时对敌人造成伤害;EnemyHitBox 也可命中此 Layer(敌人互伤,HitBox 运行时排除自身根节点) |
EnemyHitBox |
敌人武器 HitBox / LethalTrap 主体 |
发出伤害的碰撞体;命中 PlayerHurtBox 时触发对玩家的伤害判定 |
EnemyProjectile |
敌人发射的投射物 | 敌人子弹;不与 EnemyHurtBox 碰撞,命中 PlayerHurtBox 或 Ground 时有效 |
2.3 地形相关
| Layer 名称 | 挂载对象 | 用途说明 |
|---|---|---|
Ground |
静态地面、移动平台、Tilemap 地形、障碍物 | 玩家与敌人站立的实体地面;PlayerMovement 的 _groundLayer、PlayerWallDetector 的 _wallLayer(默认)均包含此 Layer;投射物命中后销毁 |
Wall |
墙壁碰撞体(垂直面) | 玩家攀墙检测(PlayerWallDetector 默认掩码包含 Wall 和 Ground);若地形使用统一的 Tilemap 则墙壁可合并到 Ground Layer |
2.4 触发区域
| Layer 名称 | 挂载对象 | 用途说明 |
|---|---|---|
TriggerZone |
存档点(CheckpointMarker)、存档台(SavePoint)、传送站(TeleportStation)、房间过渡(RoomTransition / DoorTransition)、相机区域(CameraArea) |
纯触发碰撞体(isTrigger),不参与物理阻挡;统一使用此 Layer 方便在碰撞矩阵中集中管理 |
2.5 环境危险
| Layer 名称 | 挂载对象 | 用途说明 |
|---|---|---|
HazardHitBox |
对双方均造成伤害的环境危险(落石、熔岩、毒区、AOE 爆炸范围等) | 同时与 PlayerHurtBox 和 EnemyHurtBox 碰撞;区别于 EnemyHitBox(只对玩家),此 Layer 用于阵营中立的环境伤害源 |
2.6 特殊用途
| Layer 名称 | 挂载对象 | 用途说明 |
|---|---|---|
PhantomBody |
幽灵/幻象角色的实体碰撞体 | PhantomInteractable 通过 other.gameObject.layer == PhantomBody 判断是否为幻象角色触发交互 |
3. Physics 2D 碰撞矩阵
以下为项目期望的碰撞配置,由 Physics2DLayerReport 工具维护与校验。
| Layer A | Layer B | 应碰撞 | 说明 |
|---|---|---|---|
PlayerHitBox |
EnemyHurtBox |
✅ | 玩家攻击伤害敌人 |
EnemyHitBox |
PlayerHurtBox |
✅ | 敌人攻击伤害玩家 |
EnemyHitBox |
EnemyHurtBox |
✅ | 敌人可互相伤害(HitBox 运行时排除自身根节点) |
Player |
Ground |
✅ | 玩家站在地面上 |
Enemy |
Ground |
✅ | 敌人站在地面上 |
PlayerProjectile |
EnemyHurtBox |
✅ | 玩家投射物伤害敌人 |
PlayerProjectile |
PlayerHurtBox |
❌ | 玩家投射物不自伤 |
PlayerProjectile |
Ground |
✅ | 玩家投射物命中地形 |
EnemyProjectile |
PlayerHurtBox |
✅ | 敌人投射物伤害玩家 |
EnemyProjectile |
EnemyHurtBox |
❌ | 敌人投射物不自伤 |
EnemyProjectile |
Ground |
✅ | 敌人投射物命中地形 |
PlayerHitBox |
PlayerHurtBox |
❌ | 玩家不自伤 |
PlayerProjectile |
EnemyProjectile |
❌ | 子弹不互相碰撞(Clash 系统单独处理) |
HazardHitBox |
PlayerHurtBox |
✅ | 环境危险伤害玩家 |
HazardHitBox |
EnemyHurtBox |
✅ | 环境危险伤害敌人(中立伤害) |
HazardHitBox |
PlayerHitBox |
❌ | 环境不触发拼刀 |
HazardHitBox |
EnemyHitBox |
❌ | 环境不触发拼刀 |
未在上表中列出的 Layer 对默认继承 Unity 全局设置(默认全部碰撞)。
4. 使用规范
4.1 Layer 赋值方式
- 优先使用
SceneObjectPlacerTool(菜单BaseGames → Scene → Place → …)自动赋值,避免遗漏。 - 手动创建 GameObject 时,在 Inspector 顶部 Layer 下拉菜单中选择对应 Layer。
- 代码中引用时,通过
LayerMask.NameToLayer("LayerName")获取整型索引,或通过LayerMask.GetMask("LayerA", "LayerB")获取掩码值。
// 推荐:缓存到字段,避免每帧调用
private int _playerLayer;
void Awake()
{
_playerLayer = LayerMask.NameToLayer("Player");
}
4.2 子节点继承规则
- 玩家 / 敌人根节点与其 HurtBox、HitBox 必须分别挂不同 Layer,不可统一使用根节点 Layer。
LethalTrap(陷阱):主体用EnemyHitBox,可弹起的子HurtBox用EnemyHurtBox。- 触发区域对象(存档点等)统一用
TriggerZone,不需要额外子节点分层。
4.3 禁止事项
- 禁止在代码中硬编码 Layer 整型编号(如
layer == 8),必须使用名称转换。 - 禁止直接修改
ProjectSettings/TagManager.asset中的 Layer 顺序(会导致所有已赋值对象 Layer 错乱)。 - 禁止将
TriggerZone对象的 Collider 设置为非 trigger(isTrigger必须为true)。
6. 复杂场景处理模式
6.1 弹反投射物(EnemyProjectile → 伤害敌人)
问题:EnemyProjectile ↔ EnemyHurtBox = 不碰撞,弹反后的投射物无法对敌人造成伤害。
正确方案:运行时 Layer 切换(不新增 Layer)
弹反成功时,将投射物的 Layer 从 EnemyProjectile 切换为 PlayerProjectile,同时反转飞行方向。Projectile 已有 SetFactionLayer(int ownerLayer) 方法,弹反逻辑只需调用它:
// HurtBox.ReceiveDamage() 弹反成功后(步骤2)
if (_parrySystem.ConsumeParry())
{
// 若攻击来源是投射物,翻转其阵营 Layer
if (info.SourceProjectile != null)
{
info.SourceProjectile.ReflectAsPlayerProjectile();
// ReflectAsPlayerProjectile() 内部:
// gameObject.layer = LayerMask.NameToLayer("PlayerProjectile");
// rb.velocity = -rb.velocity; // 反向
}
return;
}
注意:当前
DamageInfo尚未携带SourceProjectile字段,实现此功能需要:
- 在
DamageInfo中添加Projectile SourceProjectile字段HitBox.OnTriggerEnter2D通过_attackerTransform.GetComponent<Projectile>()填入Projectile实现ReflectAsPlayerProjectile()方法
为什么不新建 ParriedProjectile 层?
PlayerProjectile 的语义本就是"对敌人有效、对玩家无效"的攻击来源,弹反后的投射物完全符合此语义,无需新层。
6.2 环境伤害(同时对玩家与敌人有效)
LethalTrap(陷阱)当前使用 EnemyHitBox 层,只对玩家生效,是有意为之的设计(跑酷陷阱不伤敌人)。
对于确实需要同时伤害双方的环境机关(落石、熔岩、AOE 区域等),使用 HazardHitBox 层并配合 HazardHitBoxTrigger 组件(待实现),收到碰撞时向 PlayerHurtBox 和 EnemyHurtBox 均发送伤害:
HazardHitBox ↔ PlayerHurtBox → 碰撞
HazardHitBox ↔ EnemyHurtBox → 碰撞
6.3 敌人之间互伤(友伤/AOE)
EnemyHitBox ↔ EnemyHurtBox = 碰撞 已在碰撞矩阵中开启。
自伤防护由 HitBox.OnTriggerEnter2D 的根节点比较负责:
if (other.transform.root == _attackerTransform.root) return; // 排除自身
这意味着:
- 同一 GameObject 树的 EnemyHitBox 不会命中自身的 EnemyHurtBox ✅
- 敌人 A 的 HitBox 可以命中敌人 B 的 HurtBox ✅(天然支持友伤)
- Boss 的 AOE 技能可以对场景中所有其他敌人造成伤害 ✅
6.4 不需要细分的场景
以下场景不需要新增 Layer,通过 DamageInfo 字段在逻辑层区分即可:
| 场景 | 处理方式 |
|---|---|
| 区分攻击来源(玩家技能 vs 玩家普攻) | DamageInfo.SkillId / DamageInfo.SourceId |
| 不同类型伤害(物理 vs 魔法) | DamageInfo.Type(DamageType 枚举) |
| Boss 阶段专属伤害规则 | DamageInfo.Tags(DamageTags 标记位) |
| 不可弹反的攻击 | DamageInfo.Flags 不含 CanBeParried |
| 穿透无敌帧的伤害 | DamageInfo.Flags 含 IgnoreIFrame |
项目提供了 Physics2DLayerReport 编辑器工具,位于菜单:
BaseGames → Tools → Physics2D Layer Matrix → Check
BaseGames → Tools → Physics2D Layer Matrix → Auto Fix
- Check:打印当前碰撞矩阵与期望配置的对比报告。
- ✅ 正常
- ❌ 当前配置与期望不符(需修复)
- ⚠ Layer 不存在(需先在 Tags and Layers 中创建)
- Auto Fix:自动将所有不符合期望的碰撞对修正为正确值,并持久化到
ProjectSettings。
建议在以下时机运行 Check:新人入职初始化环境、合并来自其他分支的
ProjectSettings变更后、新增 Layer 之后。