Files
zeling_v2/Docs/Design/40_LiquidSwimSystem.md
2026-05-08 11:04:00 +08:00

13 KiB
Raw Blame History

40 · 液态区域与游泳系统Liquid & Swim System

命名空间 BaseGames.World.Liquid
所属文档集 ← 返回索引 · 总览
依赖 BaseGames.PlayerPlayerController · AbilitiesSaveData· BaseGames.Core.Events
关联 08_WorldSystem §6HazardZone· 03_PlayerSystem §4PlayerState FSM· 31_SaveDataSchema §2abilities.swim


目录

  1. 系统总览
  2. LiquidZone — 液态区域组件
  3. 液体类型与效果表
  4. SwimState — 游泳状态
  5. 玩家进出液体流程
  6. 酸液/熔岩特化InstantKill 液体)
  7. 浮力与水下物理参数
  8. 水下视觉效果
  9. SaveData 集成
  10. 事件频道
  11. 编辑器友好设计

1. 系统总览

液体系统处理地图中的水体、酸液、熔岩等可进入区域,解决:

  • 水体:进入后切换为游泳移动模式(SwimState),需要 swim 能力解锁
  • 酸液/熔岩:接触即触发即死(委托 HazardZone InstantKill,见 08_WorldSystem §6
  • 浮力:进入液体后重力系数降低,玩家可上浮/下沉
  • 水面特效:进出液体时溅水粒子、水下后处理 Profile
液体系统职责:
  ├─ LiquidZone         → 标记液态区域类型、触发进出液体事件
  ├─ SwimState          → PlayerController FSM 中的水下游泳状态
  ├─ LiquidPhysicsConfig → ScriptableObject水下物理参数
  └─ WaterSurfaceEffect  → 进出水面溅水粒子 + 音效

零耦合原则LiquidZone 通过 SO 事件频道发布进出液体事件,PlayerController 订阅后自行切换状态,互不直接引用。


2. LiquidZone — 液态区域组件

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 进入 → 切换 SwimStateswim 解锁) swim swim 时浮于水面(触发溺水计时)
ShallowWater 仅减速,不切换 SwimState无需能力 水深 ≤ 1.5 tile适用于浅滩、水坑
Acid 接触即死HazardZone InstantKill 效果与 HazardZone 完全相同LiquidZone 仅负责视觉层
Lava 接触即死 + 持续灼烧特效 同上,另加火焰粒子
Mud 减速 50%,不切换状态 可结合谜题(踩过留脚印 SaveData 标记)

4. SwimState — 游泳状态

SwimState 插入 PlayerControllerBase 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)  ← 负责致死逻辑

LiquidZoneAcid/LavaOnTriggerEnter2D发布 _onPlayerEntered 切换状态事件,只播放视觉反馈;致死由 HazardZone 独立处理,避免逻辑重叠。


7. 浮力与水下物理参数

LiquidPhysicsConfigSO

[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 组件

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

"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 字段;区域是否探索由 MapSystemdiscoveredRooms)统一管理。


10. 事件频道

频道 SO 类型 用途
OnPlayerEnteredLiquid LiquidEventChannelSOpayload: LiquidZone 玩家进入液体 → PlayerController 切换状态
OnPlayerExitedLiquid LiquidEventChannelSOpayload: 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