13 KiB
40 · 液态区域与游泳系统(Liquid & Swim System)
命名空间
BaseGames.World.Liquid
所属文档集 ← 返回索引 · 总览
依赖BaseGames.Player(PlayerController · AbilitiesSaveData)·BaseGames.Core.Events
关联 08_WorldSystem §6(HazardZone)· 03_PlayerSystem §4(PlayerState FSM)· 31_SaveDataSchema §2(abilities.swim)
目录
- 系统总览
- LiquidZone — 液态区域组件
- 液体类型与效果表
- SwimState — 游泳状态
- 玩家进出液体流程
- 酸液/熔岩特化(InstantKill 液体)
- 浮力与水下物理参数
- 水下视觉效果
- SaveData 集成
- 事件频道
- 编辑器友好设计
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 |
进入 → 切换 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 能力:
- 进入
WaterDangerState(新增子状态) - 玩家可在水面漂浮(按住 Jump 保持)
- 启动 溺水计时器(
drownTime秒,默认 3.0s,可配置) - 计时器归零 → 触发
OnPlayerDied(与其他即死处理一致) - 若玩家在溺水期间离开液体,计时器重置
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
[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 组件
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 字段;区域是否探索由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