245 lines
14 KiB
Markdown
245 lines
14 KiB
Markdown
# 57 · 物理层矩阵(Physics Layer Matrix)
|
||
|
||
> **命名空间** `BaseGames.Physics`(配置层)
|
||
> **所属文档集** [← 返回索引](./README.md) · [总览](./00_Overview.md)
|
||
> **关联** 04_CombatSystem(HitBox/HurtBox)· 06_EnemySystem · 13_ProjectileSystem · 24_GroundDetectionSystem · 40_LiquidSwimSystem
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [Layer 定义总表](#1-layer-定义总表)
|
||
2. [碰撞矩阵(Layer Collision Matrix)](#2-碰撞矩阵layer-collision-matrix)
|
||
3. [Physics Material 目录](#3-physics-material-目录)
|
||
4. [Rigidbody2D 使用规范](#4-rigidbody2d-使用规范)
|
||
5. [Trigger vs Collider 使用规范](#5-trigger-vs-collider-使用规范)
|
||
6. [Layer 相关代码规范](#6-layer-相关代码规范)
|
||
7. [常见错误与排查](#7-常见错误与排查)
|
||
|
||
---
|
||
|
||
## 1. Layer 定义总表
|
||
|
||
Unity 物理层(Layer 0~31),固定分配,不允许随意挪用:
|
||
|
||
| Layer ID | 名称 | 用途 |
|
||
|---------|------|------|
|
||
| 0 | `Default` | 场景中无特殊需求的静态物件(不参与物理计算的装饰物)|
|
||
| 1 | `TransparentFX` | Unity 内置,粒子特效(不参与碰撞)|
|
||
| 2 | `Ignore Raycast` | Unity 内置,不参与任何 Raycast |
|
||
| 3 | `(保留)` | — |
|
||
| 4 | `Water` | Unity 内置(本项目不使用,用 LiquidZone 代替)|
|
||
| 5 | `UI` | Unity 内置,UI 层 |
|
||
| 6 | `(保留)` | — |
|
||
| 7 | `(保留)` | — |
|
||
| **8** | `Ground` | 地面、平台(玩家/敌人站立的实体碰撞)|
|
||
| **9** | `OneWayPlatform` | 单向平台(可从下方穿越,从上方落下后站立)|
|
||
| **10** | `Wall` | 垂直墙壁(可抓附的墙体)|
|
||
| **11** | `Hazard` | 伤害区域(荆棘/熔岩地面/毒液,无碰撞体阻挡,仅触发 Trigger)|
|
||
| **12** | `Player` | 玩家 Prefab 主体 Collider |
|
||
| **13** | `PlayerHitBox` | 玩家攻击判定区(HitBox)|
|
||
| **14** | `PlayerHurtBox` | 玩家受击区(HurtBox)|
|
||
| **15** | `Enemy` | 敌人主体 Collider |
|
||
| **16** | `EnemyHitBox` | 敌人攻击判定区(HitBox)|
|
||
| **17** | `EnemyHurtBox` | 敌人受击区(HurtBox)|
|
||
| **18** | `Projectile` | 弹射物(玩家射出的子弹、Boss 弹幕)|
|
||
| **19** | `EnemyProjectile` | 敌人弹射物(与 Projectile 分层,防止玩家弹射物误伤自己)|
|
||
| **20** | `ParryTarget` | 可弹反的弹射物(HurtBox 对 Parry 检测,见 05_ParrySystem)|
|
||
| **21** | `Interactable` | 可交互物件触发区(IInteractable Trigger)|
|
||
| **22** | `LiquidZone` | 液态区域(Trigger,见 40_LiquidSwimSystem)|
|
||
| **23** | `AbilityGate` | 能力门触发区(Trigger,见 14_ProgressionSystem)|
|
||
| **24** | `Pickup` | 掉落物/收集品(Trigger,靠近自动吸附)|
|
||
| **25** | `Room` | 房间边界触发区(Trigger,触发场景加载/卸载)|
|
||
| **26** | `CameraZone` | Cinemachine 约束区域(Trigger,不参与物理)|
|
||
| **27** | `VFX` | VFX 粒子(仅视觉,不参与碰撞)|
|
||
| **28** | `NavMesh` | 寻路 Agent(PathBerserker2d 专属,不与普通物理交互)|
|
||
| **29** | `MagicWall` | 魔法障壁(`Ghost` 层忽略此层,实现太虚斩/地行术穿越)|
|
||
| **30** | `Ghost` | 玩家施放太虚斩/地行术激活期间切换到此层;忽略 `MagicWall` 碰撞 |
|
||
| **31** | `PhantomBody` | 残阴术灵体 Rigidbody2D;可触发 `PhantomInteractable`,忽略其余实体碰撞 |
|
||
|
||
---
|
||
|
||
## 2. 碰撞矩阵(Layer Collision Matrix)
|
||
|
||
**✅ = 相互检测碰撞 / Trigger 触发** | **─ = 忽略(不检测)**
|
||
|
||
> 仅列出有意义的组合,其余均为 `─`。
|
||
|
||
| | Ground | OWPlatform | Wall | Hazard | Player | PlayerHB | PlayerHurtB | Enemy | EnemyHB | EnemyHurtB | Projectile | EnemyProj | ParryTarget | Interactable | LiquidZone | Pickup | Room |
|
||
|--|--------|-----------|------|--------|--------|---------|------------|-------|---------|-----------|-----------|----------|------------|-------------|-----------|--------|------|
|
||
| **Ground** | ─ | ─ | ─ | ─ | ✅ | ─ | ─ | ✅ | ─ | ─ | ✅ | ✅ | ─ | ─ | ─ | ─ | ─ |
|
||
| **OWPlatform** | ─ | ─ | ─ | ─ | ✅ | ─ | ─ | ✅ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ |
|
||
| **Wall** | ─ | ─ | ─ | ─ | ✅ | ─ | ─ | ✅ | ─ | ─ | ✅ | ✅ | ─ | ─ | ─ | ─ | ─ |
|
||
| **Hazard** | ─ | ─ | ─ | ─ | ✅(T) | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ |
|
||
| **Player** | ✅ | ✅ | ✅ | ✅(T) | ─ | ─ | ─ | ─ | ✅(T) | ─ | ─ | ✅(T) | ─ | ✅(T) | ✅(T) | ✅(T) | ✅(T) |
|
||
| **PlayerHitBox** | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ✅(T) | ─ | ─ | ─ | ─ | ─ | ─ | ─ |
|
||
| **PlayerHurtBox** | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ✅(T) | ─ | ─ | ✅(T) | ✅(T) | ─ | ─ | ─ | ─ |
|
||
| **Enemy** | ✅ | ✅ | ✅ | ─ | ─ | ✅(T) | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ |
|
||
| **EnemyHitBox** | ─ | ─ | ─ | ─ | ─ | ─ | ✅(T) | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ |
|
||
| **EnemyHurtBox** | ─ | ─ | ─ | ─ | ─ | ✅(T) | ─ | ─ | ─ | ─ | ✅(T) | ─ | ─ | ─ | ─ | ─ | ─ |
|
||
| **Projectile** | ✅ | ─ | ✅ | ─ | ─ | ─ | ✅(T) | ─ | ─ | ✅(T) | ─ | ─ | ─ | ─ | ─ | ─ | ─ |
|
||
| **EnemyProjectile** | ✅ | ─ | ✅ | ─ | ✅(T) | ─ | ✅(T) | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ |
|
||
| **ParryTarget** | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ | ─ |
|
||
|
||
> (T) = 仅 Trigger 检测(OnTriggerEnter2D / OnTriggerStay2D),不产生物理推力
|
||
|
||
### 新增层补充矩阵(Ghost / MagicWall / PhantomBody)
|
||
|
||
| | Ground | OWPlatform | Wall | MagicWall | Player | Enemy | Interactable | PhantomInteractable |
|
||
|--|--------|-----------|------|-----------|--------|-------|-------------|--------------------|
|
||
| **Ghost** | ✅ | ✅ | ✅ | ❌ 忽略 | ─ | ─ | ✅(T) | ✅(T) |
|
||
| **MagicWall** | ─ | ─ | ─ | ─ | ✅ | ✅ | ─ | ─ |
|
||
| **PhantomBody** | ❌ 忽略 | ❌ 忽略 | ❌ 忽略 | ❌ 忽略 | ─ | ─ | ─ | ✅(T) |
|
||
|
||
> - **Ghost**:玩家变换为幽灵层后,与 `MagicWall` 无碰撞(穿越),但仍与地面/墙壁碰撞(地行术需要站在地面上遁行)。
|
||
> - **PhantomBody**:残阴术灵体完全忽略地面/墙壁,仅 Trigger 触发 `PhantomInteractable`(`Interactable` 子层)。
|
||
> - `SoftTerrain`(松软地面)**不需要独立物理层**:使用 `Ground` 层,`GroundDiveState` 通过 `Physics2D.OverlapPoint()` 的返回值检查是否挂载 `SoftTerrain` 组件来判断地形类型。
|
||
|
||
### 矩阵关键设计决策
|
||
|
||
| 决策 | 原因 |
|
||
|------|------|
|
||
| `Enemy` 不与 `EnemyProjectile` 碰撞 | 防止 Boss 弹幕打到自己身上 |
|
||
| `Projectile` 不与 `Enemy` 碰撞(走 EnemyHurtBox)| 弹射物不推动敌人刚体,只通过 HurtBox 触发伤害 |
|
||
| `PlayerHitBox` 不与 `Ground/Wall` 碰撞 | 防止近地攻击被地面碰撞遮挡 |
|
||
| `Hazard` 只触发 `Player`,不触发 `Enemy` | 敌人在自己的陷阱中不受伤 |
|
||
| `ParryTarget` 不与任何层碰撞 | 弹反检测由 `ParrySystem` 的 `OverlapCircle` 主动查询,无需被动碰撞 |
|
||
|
||
---
|
||
|
||
## 3. Physics Material 目录
|
||
|
||
存放路径:`Assets/Physics/Materials/`
|
||
|
||
| 资产名 | Friction | Bounciness | 使用对象 |
|
||
|--------|---------|-----------|---------|
|
||
| `PhysMat_Ground` | 0.4 | 0.0 | 地面碰撞体(普通地板)|
|
||
| `PhysMat_Ice` | 0.02 | 0.0 | 冰面地块(低摩擦)|
|
||
| `PhysMat_Bounce` | 0.2 | 0.8 | 弹跳蘑菇/弹射台 |
|
||
| `PhysMat_Player` | 0.0 | 0.0 | 玩家 Rigidbody2D Collider(零摩擦防止卡墙)|
|
||
| `PhysMat_Enemy` | 0.3 | 0.0 | 敌人 Rigidbody2D Collider |
|
||
| `PhysMat_Projectile` | 0.0 | 0.0 | 弹射物(与地面/墙壁碰撞时不反弹,由代码控制反弹逻辑)|
|
||
| `PhysMat_Liquid` | 0.0 | 0.0 | 液态区域边界(仅 Trigger,但 PhysMat 仍设为零摩擦)|
|
||
|
||
### 摩擦力处理原则
|
||
|
||
- **玩家碰撞体摩擦力始终为 0**——移动完全由 `PlayerMovement` 的速度控制,不依赖物理摩擦
|
||
- 坡面行走不依赖摩擦力,而是使用地面法线修正移动方向(见 24_GroundDetectionSystem.md)
|
||
- 弹跳效果不依赖 `Bounciness` 物理材质,而是在 `OnCollisionEnter2D` 中代码施加冲量(更可控)
|
||
|
||
---
|
||
|
||
## 4. Rigidbody2D 使用规范
|
||
|
||
| 对象类型 | Body Type | Interpolation | Collision Detection | Constraints |
|
||
|---------|-----------|--------------|--------------------|----|
|
||
| **玩家** | Dynamic | Interpolate | Continuous | FreezeRotation Z |
|
||
| **跟随平台(可移动地块)**| Kinematic | Interpolate | Continuous | — |
|
||
| **普通敌人(地面)** | Dynamic | Interpolate | Discrete | FreezeRotation Z |
|
||
| **飞行敌人** | Dynamic | Interpolate | Discrete | FreezeRotation Z |
|
||
| **弹射物** | Dynamic | None | Continuous | FreezeRotation Z |
|
||
| **可破坏物件** | Dynamic(激活前 Kinematic)| None | Discrete | — |
|
||
| **触发区域(Hazard/LiquidZone 等)** | 无 Rigidbody2D | — | — | — |
|
||
|
||
**禁止行为**:
|
||
- ❌ 不在 `Update()` 中直接修改 `transform.position`(会绕过物理,用 `Rigidbody2D.MovePosition`)
|
||
- ❌ 不设置 `Rigidbody2D.velocity` 直接赋值(除非做瞬间冲刺,否则用 `AddForce`)
|
||
- ❌ 不在运行时频繁切换 Body Type(`Dynamic ↔ Kinematic` 切换有性能开销)
|
||
|
||
---
|
||
|
||
## 5. Trigger vs Collider 使用规范
|
||
|
||
| 用途 | 使用 Collider | 使用 Trigger |
|
||
|------|-------------|------------|
|
||
| 角色站立在地面上 | ✅ Collider | ❌ |
|
||
| HitBox / HurtBox 伤害检测 | ❌ | ✅ Trigger |
|
||
| 危险区域(荆棘)伤害 | ❌ | ✅ Trigger |
|
||
| 液态区域检测 | ❌ | ✅ Trigger |
|
||
| 交互提示范围(InteractionPrompt)| ❌ | ✅ Trigger |
|
||
| 房间边界(加载/卸载触发)| ❌ | ✅ Trigger |
|
||
| 镜头约束区域(Cinemachine ConfinerBound)| ✅ Collider(设为 Trigger)| 二者并存(见 Cinemachine 文档)|
|
||
| 弹射物与墙面碰撞(需反弹计算)| ✅ Collider | ❌ |
|
||
| 弹射物与 HurtBox | ❌ | ✅ Trigger |
|
||
|
||
**混合使用**(同一 Collider2D 同时作为 Collider 和 Trigger):
|
||
- ❌ **禁止**——同一 Collider 不能同时是 Collider 和 Trigger,语义混乱
|
||
- 需要两种功能时,在同一 GameObject 上添加两个 Collider2D 组件
|
||
|
||
---
|
||
|
||
## 6. Layer 相关代码规范
|
||
|
||
### 6.1 使用 LayerMask 常量,禁止硬编码数字
|
||
|
||
```csharp
|
||
// ✅ 正确
|
||
private static readonly int LayerGround = LayerMask.NameToLayer("Ground");
|
||
private static readonly LayerMask MaskGround = LayerMask.GetMask("Ground", "OneWayPlatform");
|
||
|
||
// ❌ 禁止
|
||
int layer = 8; // 硬编码 Layer ID
|
||
```
|
||
|
||
### 6.2 统一 LayerMask 常量类
|
||
|
||
```csharp
|
||
// Assets/Scripts/Core/Physics/Layers.cs
|
||
public static class Layers
|
||
{
|
||
public static readonly int Ground = LayerMask.NameToLayer("Ground");
|
||
public static readonly int OneWayPlatform = LayerMask.NameToLayer("OneWayPlatform");
|
||
public static readonly int Wall = LayerMask.NameToLayer("Wall");
|
||
public static readonly int Player = LayerMask.NameToLayer("Player");
|
||
public static readonly int PlayerHitBox = LayerMask.NameToLayer("PlayerHitBox");
|
||
public static readonly int PlayerHurtBox = LayerMask.NameToLayer("PlayerHurtBox");
|
||
public static readonly int Enemy = LayerMask.NameToLayer("Enemy");
|
||
public static readonly int EnemyHitBox = LayerMask.NameToLayer("EnemyHitBox");
|
||
public static readonly int EnemyHurtBox = LayerMask.NameToLayer("EnemyHurtBox");
|
||
public static readonly int Projectile = LayerMask.NameToLayer("Projectile");
|
||
public static readonly int EnemyProjectile = LayerMask.NameToLayer("EnemyProjectile");
|
||
public static readonly int Hazard = LayerMask.NameToLayer("Hazard");
|
||
public static readonly int LiquidZone = LayerMask.NameToLayer("LiquidZone");
|
||
public static readonly int Pickup = LayerMask.NameToLayer("Pickup");
|
||
public static readonly int Room = LayerMask.NameToLayer("Room");
|
||
|
||
// 技能专属层
|
||
public static readonly int Ghost = LayerMask.NameToLayer("Ghost"); // 太虚斩/地行术幽灵层
|
||
public static readonly int PhantomBody = LayerMask.NameToLayer("PhantomBody"); // 残阴术灵体层
|
||
public static readonly int MagicWall = LayerMask.NameToLayer("MagicWall"); // 魔法障壁(Ghost 穿越)
|
||
|
||
// 复合 Mask(常用组合)
|
||
public static readonly LayerMask MaskSolidGround = LayerMask.GetMask("Ground", "OneWayPlatform");
|
||
public static readonly LayerMask MaskWallAndGround = LayerMask.GetMask("Ground", "OneWayPlatform", "Wall");
|
||
public static readonly LayerMask MaskAllHurtBox = LayerMask.GetMask("PlayerHurtBox", "EnemyHurtBox");
|
||
public static readonly LayerMask MaskEnemy = LayerMask.GetMask("Enemy", "EnemyHurtBox");
|
||
// Ghost 状态时的地面检测(排除 MagicWall,但保留普通地面)
|
||
public static readonly LayerMask MaskGhostGround = LayerMask.GetMask("Ground", "OneWayPlatform");
|
||
}
|
||
```
|
||
|
||
### 6.3 Layer 设置检查器(CI 集成)
|
||
|
||
在 `Assets/Tests/EditMode/PhysicsLayerTests.cs` 中验证:
|
||
- 所有 Layer 名称正确注册
|
||
- Collision Matrix 中的关键对/忽略关系与本文档一致
|
||
- 玩家 Prefab 使用正确的 Layer
|
||
|
||
---
|
||
|
||
## 7. 常见错误与排查
|
||
|
||
| 症状 | 原因 | 修复 |
|
||
|------|------|------|
|
||
| 玩家无法站在地面上 | Player 与 Ground 的碰撞被关闭 | 检查 Collision Matrix |
|
||
| 攻击打不到敌人 | PlayerHitBox 与 EnemyHurtBox 碰撞关系未启用 | 勾选矩阵对应格 |
|
||
| 弹射物穿透地面 | Projectile 的 Collision Detection 设为 Discrete | 改为 Continuous |
|
||
| 危险区域不造成伤害 | Hazard 碰撞体忘记勾选 Is Trigger | 勾选 Is Trigger |
|
||
| 敌人被自己的弹幕打到 | Enemy 与 EnemyProjectile 未忽略 | 检查矩阵,确认忽略 |
|
||
| 近地面攻击被地面碰撞打断 | PlayerHitBox 与 Ground 未忽略 | 确认矩阵忽略该组合 |
|
||
| 单向平台两侧可以穿过 | OneWayPlatform 应用了标准碰撞 | 使用 `PlatformEffector2D` 组件 |
|
||
|
||
---
|
||
|
||
*本文档版本 1.0 · 2026-04 · 关联 04_CombatSystem / 13_ProjectileSystem / 24_GroundDetectionSystem*
|