chore: initial commit
This commit is contained in:
358
Docs/Design/40_LiquidSwimSystem.md
Normal file
358
Docs/Design/40_LiquidSwimSystem.md
Normal file
@@ -0,0 +1,358 @@
|
||||
# 40 · 液态区域与游泳系统(Liquid & Swim System)
|
||||
|
||||
> **命名空间** `BaseGames.World.Liquid`
|
||||
> **所属文档集** [← 返回索引](./README.md) · [总览](./00_Overview.md)
|
||||
> **依赖** `BaseGames.Player`(PlayerController · AbilitiesSaveData)· `BaseGames.Core.Events`
|
||||
> **关联** 08_WorldSystem §6(HazardZone)· 03_PlayerSystem §4(PlayerState FSM)· 31_SaveDataSchema §2(`abilities.swim`)
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [系统总览](#1-系统总览)
|
||||
2. [LiquidZone — 液态区域组件](#2-liquidzone--液态区域组件)
|
||||
3. [液体类型与效果表](#3-液体类型与效果表)
|
||||
4. [SwimState — 游泳状态](#4-swimstate--游泳状态)
|
||||
5. [玩家进出液体流程](#5-玩家进出液体流程)
|
||||
6. [酸液/熔岩特化(InstantKill 液体)](#6-酸液熔岩特化instantkill-液体)
|
||||
7. [浮力与水下物理参数](#7-浮力与水下物理参数)
|
||||
8. [水下视觉效果](#8-水下视觉效果)
|
||||
9. [SaveData 集成](#9-savedata-集成)
|
||||
10. [事件频道](#10-事件频道)
|
||||
11. [编辑器友好设计](#11-编辑器友好设计)
|
||||
|
||||
---
|
||||
|
||||
## 1. 系统总览
|
||||
|
||||
液体系统处理地图中的水体、酸液、熔岩等可进入区域,解决:
|
||||
|
||||
- **水体**:进入后切换为游泳移动模式(`SwimState`),需要 `swim` 能力解锁
|
||||
- **酸液/熔岩**:接触即触发即死(委托 `HazardZone InstantKill`,见 08_WorldSystem §6)
|
||||
- **浮力**:进入液体后重力系数降低,玩家可上浮/下沉
|
||||
- **水面特效**:进出液体时溅水粒子、水下后处理 Profile
|
||||
|
||||
```
|
||||
液体系统职责:
|
||||
├─ LiquidZone → 标记液态区域类型、触发进出液体事件
|
||||
├─ SwimState → PlayerController FSM 中的水下游泳状态
|
||||
├─ LiquidPhysicsConfig → ScriptableObject,水下物理参数
|
||||
└─ WaterSurfaceEffect → 进出水面溅水粒子 + 音效
|
||||
```
|
||||
|
||||
**零耦合原则**:`LiquidZone` 通过 SO 事件频道发布进出液体事件,`PlayerController` 订阅后自行切换状态,互不直接引用。
|
||||
|
||||
---
|
||||
|
||||
## 2. LiquidZone — 液态区域组件
|
||||
|
||||
```csharp
|
||||
namespace BaseGames.World.Liquid
|
||||
{
|
||||
/// <summary>
|
||||
/// 挂在液态区域的根 GameObject 上。
|
||||
/// 子物件 [Surface] 挂载水面特效触发器;子物件 [Body] 挂载主区域碰撞体。
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider2D))]
|
||||
public class LiquidZone : MonoBehaviour
|
||||
{
|
||||
[Header("液体类型")]
|
||||
[SerializeField] LiquidType _liquidType = LiquidType.Water;
|
||||
|
||||
[Header("物理配置")]
|
||||
[SerializeField] LiquidPhysicsConfigSO _physicsConfig;
|
||||
|
||||
[Header("事件频道")]
|
||||
[SerializeField] LiquidEventChannelSO _onPlayerEntered; // payload: LiquidZone
|
||||
[SerializeField] LiquidEventChannelSO _onPlayerExited; // payload: LiquidZone
|
||||
|
||||
[Header("视觉反馈")]
|
||||
[SerializeField] MMF_Player _splashEnterFeedback;
|
||||
[SerializeField] MMF_Player _splashExitFeedback;
|
||||
|
||||
public LiquidType Type => _liquidType;
|
||||
public LiquidPhysicsConfigSO Physics => _physicsConfig;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### LiquidZone Prefab 层级结构
|
||||
|
||||
```
|
||||
[LiquidZone_River_01]
|
||||
├── SpriteRenderer(水体图块精灵,带流动 Shader)
|
||||
├── PolygonCollider2D (IsTrigger) ← 主区域,检测 Player/Enemy
|
||||
├── LiquidZone.cs
|
||||
│
|
||||
├── [Surface] ← 水面层(分离触发器,检测进出水面)
|
||||
│ ├── BoxCollider2D (IsTrigger, 1px 高)
|
||||
│ └── WaterSurfaceEffect.cs ← 触发溅水粒子
|
||||
│
|
||||
└── [Hazard](酸液/熔岩时存在)
|
||||
└── HazardZone.cs (_hazardType = InstantKill)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 液体类型与效果表
|
||||
|
||||
| `LiquidType` | 行为 | 需要能力 | 特殊说明 |
|
||||
|-------------|------|---------|---------|
|
||||
| `Water` | 进入 → 切换 SwimState(需 `swim` 解锁)| `swim` | 无 `swim` 时浮于水面(触发溺水计时)|
|
||||
| `ShallowWater` | 仅减速,不切换 SwimState,无需能力 | 无 | 水深 ≤ 1.5 tile,适用于浅滩、水坑 |
|
||||
| `Acid` | 接触即死(HazardZone InstantKill) | — | 效果与 HazardZone 完全相同,LiquidZone 仅负责视觉层 |
|
||||
| `Lava` | 接触即死 + 持续灼烧特效 | — | 同上,另加火焰粒子 |
|
||||
| `Mud` | 减速 50%,不切换状态 | 无 | 可结合谜题(踩过留脚印 SaveData 标记)|
|
||||
|
||||
---
|
||||
|
||||
## 4. SwimState — 游泳状态
|
||||
|
||||
`SwimState` 插入 `PlayerController` 的 **Base Layer FSM**,在 `InWater` 分支下工作:
|
||||
|
||||
```
|
||||
Base Layer FSM(新增分支):
|
||||
AnyState ─── OnEnteredWater ──► SwimState
|
||||
(LiquidType.Water && HasAbility.Swim)
|
||||
├─ SwimIdleState
|
||||
├─ SwimMoveState
|
||||
├─ SwimDiveState
|
||||
└─ SwimSurfaceState
|
||||
SwimState ─── OnExitedWater ──► FallState(离开液体继承速度)
|
||||
```
|
||||
|
||||
### SwimState 子状态
|
||||
|
||||
| 子状态 | 触发条件 | 说明 |
|
||||
|--------|---------|------|
|
||||
| `SwimIdleState` | 无方向输入 | 缓慢下沉(可配置),循环游泳动画 |
|
||||
| `SwimMoveState` | 有方向输入(↑↓←→) | 8 向移动,速度受 `swimSpeed` 限制 |
|
||||
| `SwimDiveState` | 按住 ↓ | 快速下潜,速度 × `diveSpeedMultiplier` |
|
||||
| `SwimSurfaceState` | 接近水面 + 按 Jump | 跃出水面,施加 `surfaceJumpForce` 冲量 |
|
||||
|
||||
### 无 `swim` 能力时的溺水行为
|
||||
|
||||
若玩家进入 `LiquidType.Water` 时 **未解锁 `swim` 能力**:
|
||||
|
||||
1. 进入 `WaterDangerState`(新增子状态)
|
||||
2. 玩家可在水面漂浮(按住 Jump 保持)
|
||||
3. 启动 **溺水计时器**(`drownTime` 秒,默认 3.0s,可配置)
|
||||
4. 计时器归零 → 触发 `OnPlayerDied`(与其他即死处理一致)
|
||||
5. 若玩家在溺水期间离开液体,计时器重置
|
||||
|
||||
---
|
||||
|
||||
## 5. 玩家进出液体流程
|
||||
|
||||
### 进入液体
|
||||
|
||||
```
|
||||
Player Rigidbody2D 进入 LiquidZone PolygonCollider2D(IsTrigger)
|
||||
→ LiquidZone.OnTriggerEnter2D(Player)
|
||||
├─ 播放 _splashEnterFeedback(MMF_Player:溅水粒子 + 入水音效)
|
||||
├─ 发布 _onPlayerEntered 频道(payload: this LiquidZone)
|
||||
└─ (HazardZone 子物件自动处理 InstantKill 逻辑,互不干扰)
|
||||
|
||||
PlayerController 监听 _onPlayerEntered:
|
||||
→ 查询 _liquidType
|
||||
├─ Water:检查 PlayerStats.HasAbility(swim)
|
||||
│ ├─ [有] → FSM 切换至 SwimState
|
||||
│ └─ [无] → FSM 切换至 WaterDangerState(溺水倒计时)
|
||||
├─ ShallowWater:应用 LiquidPhysicsConfig.shallowSpeedScale
|
||||
└─ Mud:应用 LiquidPhysicsConfig.mudSpeedScale
|
||||
```
|
||||
|
||||
### 离开液体
|
||||
|
||||
```
|
||||
Player Rigidbody2D 退出 LiquidZone(OnTriggerExit2D)
|
||||
→ LiquidZone 发布 _onPlayerExited 频道
|
||||
|
||||
PlayerController:
|
||||
→ 恢复正常 Gravity Scale(从 LiquidPhysicsConfig.underwaterGravityScale 恢复)
|
||||
→ FSM 切换至 FallState(继承当前 Rigidbody2D.velocity)
|
||||
→ 播放 _splashExitFeedback
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 酸液/熔岩特化(InstantKill 液体)
|
||||
|
||||
酸液和熔岩本质上是 **视觉增强的 HazardZone**,实际致死逻辑复用 `HazardZone(InstantKill)`:
|
||||
|
||||
```
|
||||
[AcidZone_Cave_01]
|
||||
├── SpriteRenderer(酸液精灵 + 涌动 Shader)
|
||||
├── LiquidZone.cs (_liquidType = Acid) ← 负责视觉特效触发
|
||||
├── [Surface]
|
||||
│ └── WaterSurfaceEffect.cs(酸液飞溅粒子)
|
||||
└── [Hazard]
|
||||
└── HazardZone.cs (_hazardType = InstantKill) ← 负责致死逻辑
|
||||
```
|
||||
|
||||
`LiquidZone`(Acid/Lava)在 `OnTriggerEnter2D` 中**不**发布 `_onPlayerEntered` 切换状态事件,只播放视觉反馈;致死由 `HazardZone` 独立处理,避免逻辑重叠。
|
||||
|
||||
---
|
||||
|
||||
## 7. 浮力与水下物理参数
|
||||
|
||||
### LiquidPhysicsConfigSO
|
||||
|
||||
```csharp
|
||||
[CreateAssetMenu(menuName = "World/Liquid/LiquidPhysicsConfig")]
|
||||
public class LiquidPhysicsConfigSO : ScriptableObject
|
||||
{
|
||||
[Header("水下重力")]
|
||||
public float underwaterGravityScale = 0.3f; // 正常重力 × 0.3 → 缓慢下沉
|
||||
public float sinkSpeed = 1.5f; // 无操作时下沉速度上限 (u/s)
|
||||
|
||||
[Header("游泳速度")]
|
||||
public float swimSpeed = 5.0f; // 最大游泳速度 (u/s)
|
||||
public float swimAcceleration = 12.0f; // 加速度
|
||||
public float diveSpeedMultiplier = 1.8f; // 下潜速度倍率
|
||||
|
||||
[Header("跃出水面")]
|
||||
public float surfaceJumpForce = 12.0f; // 出水跳跃冲量 Y 分量
|
||||
|
||||
[Header("浅水/泥泞")]
|
||||
public float shallowSpeedScale = 0.65f; // 浅水移动速度缩放
|
||||
public float mudSpeedScale = 0.50f; // 泥泞移动速度缩放
|
||||
|
||||
[Header("溺水")]
|
||||
public float drownTime = 3.0f; // 无能力时溺水计时(秒)
|
||||
}
|
||||
```
|
||||
|
||||
### 参考调优值(游戏手感)
|
||||
|
||||
| 参数 | 推荐值 | 说明 |
|
||||
|------|-------|------|
|
||||
| `underwaterGravityScale` | 0.25–0.35 | 越小越"飘",Hollow Knight 水感约 0.3 |
|
||||
| `swimSpeed` | 4.5–5.5 | 略低于地面奔跑速度(约 70%)|
|
||||
| `surfaceJumpForce` | 10–14 | 跃出水面感觉应比普通跳跃稍低 |
|
||||
| `drownTime` | 2.5–3.5 | 太短会惩罚探索;太长失去压迫感 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 水下视觉效果
|
||||
|
||||
### 水下后处理 Profile
|
||||
|
||||
进入 `LiquidType.Water` 时,`LiquidZone` 通过 SO 事件频道触发 Post-processing Volume 切换:
|
||||
|
||||
```
|
||||
_onPlayerEntered 广播
|
||||
→ UnderwaterPostProcessingController 监听
|
||||
→ 激活 Volume_Underwater(Color Adjustment:青绿色 Filter + 亮度 -0.3)
|
||||
→ 激活 ChromaticAberration(intensity = 0.4,模拟折射)
|
||||
→ 屏幕边缘 Vignette 增强(intensity = 0.45)
|
||||
|
||||
_onPlayerExited 广播
|
||||
→ UnderwaterPostProcessingController
|
||||
→ 平滑过渡回 Volume_Default(过渡时长 0.4s)
|
||||
```
|
||||
|
||||
### WaterSurfaceEffect 组件
|
||||
|
||||
```csharp
|
||||
public class WaterSurfaceEffect : MonoBehaviour
|
||||
{
|
||||
[SerializeField] MMF_Player _playerEnterFeedback; // 入水溅水粒子 + 音效
|
||||
[SerializeField] MMF_Player _playerExitFeedback; // 出水溅水粒子
|
||||
|
||||
void OnTriggerEnter2D(Collider2D col)
|
||||
{
|
||||
if (col.CompareTag("Player"))
|
||||
_playerEnterFeedback?.PlayFeedbacks(col.transform.position);
|
||||
}
|
||||
|
||||
void OnTriggerExit2D(Collider2D col)
|
||||
{
|
||||
if (col.CompareTag("Player"))
|
||||
_playerExitFeedback?.PlayFeedbacks(col.transform.position);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. SaveData 集成
|
||||
|
||||
`abilities.swim` 字段由 `AbilitiesSaveData` 负责(见 31_SaveDataSchema_Unified §3):
|
||||
|
||||
```json
|
||||
"abilities": {
|
||||
"doubleJump": false,
|
||||
"wallGrab": false,
|
||||
"dash": false,
|
||||
"airDash": false,
|
||||
"groundSlam": false,
|
||||
"swim": false
|
||||
}
|
||||
```
|
||||
|
||||
- 初始值为 `false`(游戏开始时无游泳能力)
|
||||
- 通过 `AbilityUnlock`(见 08_WorldSystem §8)交互后 `PlayerStats.UnlockAbility(AbilityType.Swim)` 写入存档
|
||||
- 读档时 `PlayerStats.Init(SaveData)` 将 `swim` 字段同步到 `PlayerStats._abilities` 集合
|
||||
|
||||
> **注意**:`LiquidZone` 本身不持有 SaveData 字段;区域是否探索由 `MapSystem`(`discoveredRooms`)统一管理。
|
||||
|
||||
---
|
||||
|
||||
## 10. 事件频道
|
||||
|
||||
| 频道 SO | 类型 | 用途 |
|
||||
|---------|------|------|
|
||||
| `OnPlayerEnteredLiquid` | `LiquidEventChannelSO`(payload: `LiquidZone`)| 玩家进入液体 → PlayerController 切换状态 |
|
||||
| `OnPlayerExitedLiquid` | `LiquidEventChannelSO`(payload: `LiquidZone`)| 玩家离开液体 → 恢复正常物理 |
|
||||
| `OnPlayerDrownStart` | `VoidEventChannelSO` | 溺水计时开始 → HUD 显示溺水警告UI |
|
||||
| `OnPlayerDrownCancel` | `VoidEventChannelSO` | 溺水计时取消(离开水体)|
|
||||
|
||||
```
|
||||
LiquidEventChannelSO.cs:
|
||||
[CreateAssetMenu(menuName = "Events/LiquidEventChannel")]
|
||||
public class LiquidEventChannelSO : ScriptableObject
|
||||
{
|
||||
public event Action<LiquidZone> OnEventRaised;
|
||||
public void Raise(LiquidZone zone) => OnEventRaised?.Invoke(zone);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 编辑器友好设计
|
||||
|
||||
### Custom Inspector(LiquidZone)
|
||||
|
||||
```
|
||||
LiquidZone Inspector:
|
||||
[液体属性]
|
||||
LiquidType: ● Water ○ ShallowWater ○ Acid ○ Lava ○ Mud
|
||||
Physics Config: [LiquidPhysicsConfigSO 引用]
|
||||
[事件频道] (折叠)
|
||||
[视觉反馈]
|
||||
SplashEnter: [MMF_Player 引用]
|
||||
SplashExit: [MMF_Player 引用]
|
||||
─────────────────────────────
|
||||
[▶ 预览入水特效] [▶ 预览出水特效] ← Editor Only 按钮
|
||||
```
|
||||
|
||||
### Gizmos
|
||||
|
||||
- 编辑模式下 `LiquidZone` 用**半透明蓝色**(Water)/**半透明绿色**(Acid)/**半透明红色**(Lava)填充 PolygonCollider2D 范围
|
||||
- `WaterSurfaceEffect` 的 1px 触发器在 Scene 视图中显示为**青色细线**
|
||||
|
||||
### 关卡搭建建议
|
||||
|
||||
```
|
||||
Layer 命名约定:
|
||||
Tilemap_Liquid_Water ← 水体图块(仅视觉,无碰撞)
|
||||
Tilemap_Liquid_Surface ← 水面装饰图块(涟漪动画)
|
||||
|
||||
LiquidZone 组件挂在独立 GameObject 上(非 Tilemap),
|
||||
PolygonCollider2D 手动覆盖液体区域轮廓,精度 ≥ 2 顶点/格。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*文档版本 1.0 · 2025*
|
||||
Reference in New Issue
Block a user