# 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 { /// /// 挂在液态区域的根 GameObject 上。 /// 子物件 [Surface] 挂载水面特效触发器;子物件 [Body] 挂载主区域碰撞体。 /// [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 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*