chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View 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 §6HazardZone· 03_PlayerSystem §4PlayerState 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 PolygonCollider2DIsTrigger
→ LiquidZone.OnTriggerEnter2D(Player)
├─ 播放 _splashEnterFeedbackMMF_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 退出 LiquidZoneOnTriggerExit2D
→ LiquidZone 发布 _onPlayerExited 频道
PlayerController:
→ 恢复正常 Gravity Scale从 LiquidPhysicsConfig.underwaterGravityScale 恢复)
→ FSM 切换至 FallState继承当前 Rigidbody2D.velocity
→ 播放 _splashExitFeedback
```
---
## 6. 酸液/熔岩特化InstantKill 液体)
酸液和熔岩本质上是 **视觉增强的 HazardZone**,实际致死逻辑复用 `HazardZoneInstantKill`
```
[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.250.35 | 越小越"飘"Hollow Knight 水感约 0.3 |
| `swimSpeed` | 4.55.5 | 略低于地面奔跑速度(约 70%|
| `surfaceJumpForce` | 1014 | 跃出水面感觉应比普通跳跃稍低 |
| `drownTime` | 2.53.5 | 太短会惩罚探索;太长失去压迫感 |
---
## 8. 水下视觉效果
### 水下后处理 Profile
进入 `LiquidType.Water` 时,`LiquidZone` 通过 SO 事件频道触发 Post-processing Volume 切换:
```
_onPlayerEntered 广播
→ UnderwaterPostProcessingController 监听
→ 激活 Volume_UnderwaterColor Adjustment青绿色 Filter + 亮度 -0.3
→ 激活 ChromaticAberrationintensity = 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 InspectorLiquidZone
```
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*