9.2 KiB
9.2 KiB
01 · 输入系统
命名空间
BaseGames.Input
所属文档集 ← 返回索引 · 总览
依赖 Unity Input System ·BaseGames.Core.Events
目录
1. 设计目标
- 零耦合:游戏系统不直接引用
PlayerInput组件,通过InputReaderSO(ScriptableObject)订阅输入事件 - 可测试:
InputReaderSO可在测试代码中手动触发事件,无需模拟硬件 - 可重映射:Input Actions Asset 支持运行时重映射,配合 UI 设置页面
- 输入宽容:提供输入缓冲(Buffer)和 Coyote Time,提升手感
2. 核心架构:InputReaderSO
InputReaderSO 是整个输入系统的唯一门面,封装 Input Actions,以 C# Action 事件对外暴露:
InputReaderSO (ScriptableObject)
│
├── 内部持有: PlayerInputActions (生成的 C# 类)
│
├── 移动输入
│ ├── event MoveEvent(Vector2 direction)
│ └── Vector2 MoveInput { get; } ← 当前帧移动向量(持续值)
│
├── 跳跃输入
│ ├── event JumpStartedEvent() ← 按下(用于触发跳跃)
│ └── event JumpCancelledEvent() ← 松开(用于可变跳跃高度)
│
├── 攻击输入
│ └── event AttackEvent() ← 按下
│
├── 弹反输入
│ └── event ParryEvent() ← 按下
│
├── 冲刺输入
│ └── event DashEvent() ← 按下
│
├── 灵泉输入
│ └── event UseSpringEvent() ← 按下(消耗灵泉使用次数)
│
├── 形态切换输入
│ ├── event SwitchSkyFormEvent() ← 切换天魂姿态
│ ├── event SwitchEarthFormEvent() ← 切换地魂姿态
│ └── event SwitchDeathFormEvent() ← 切换命魂姿态
│
├── 技能输入
│ ├── event SoulSkillEvent() ← 当前形态魂技能(消耗灵力)
│ ├── event SpiritSkill1StartedEvent() ← 魄技能 1 按下
│ ├── event SpiritSkill1CancelledEvent() ← 魄技能 1 松开(蓄力型技能用)
│ ├── event SpiritSkill2StartedEvent() ← 魄技能 2 按下
│ └── event SpiritSkill2CancelledEvent() ← 魄技能 2 松开(蓄力型技能用)
│
├── 交互输入
│ └── event InteractEvent() ← 按下
│
├── UI 输入
│ ├── event PauseEvent()
│ ├── event NavigateEvent(Vector2 dir)
│ └── event SubmitEvent()
│
└── Action Map 切换
├── EnableGameplayInput() ← 进入游戏时启用
├── EnableUIInput() ← 进入 UI 时启用
└── DisableAllInput() ← 过场动画/加载时禁用
所有使用输入的系统仅需在 Inspector 中拖入 InputReaderSO 资产,订阅所需事件:
系统 A: [SerializeField] InputReaderSO _input;
OnEnable → _input.JumpStartedEvent += HandleJump;
OnDisable → _input.JumpStartedEvent -= HandleJump;
3. Input Actions 资产结构
PlayerInputActions.inputactions 包含以下 Action Maps:
Gameplay Action Map
| Action 名称 | 类型 | 绑定(默认键盘) | 绑定(手柄) |
|---|---|---|---|
Move |
Value (Vector2) | WASD / Arrow Keys | 左摇杆 |
Jump |
Button | Space | South Button (×/A) |
Attack |
Button | J / Z | West Button (□/X) |
Parry |
Button | K / X | Right Bumper (R1/RB) |
Dash |
Button | L-Shift / C | East Button (○/B) |
UseSpring |
Button | G / Tab | Right Trigger (R2/RT) |
Interact |
Button | F / E | North Button (△/Y) |
SwitchSkyForm |
Button | 1 | D-Pad Left |
SwitchEarthForm |
Button | 2 | D-Pad Down |
SwitchDeathForm |
Button | 3 | D-Pad Right |
SoulSkill |
Button | Q | Left Trigger (L2/LT) |
SpiritSkill1 |
Button | E | D-Pad Up |
SpiritSkill2 |
Button | R | Left Trigger (L2/LT)(双击或组合) |
Pause |
Button | Escape | Start / Menu |
UI Action Map
| Action 名称 | 类型 | 说明 |
|---|---|---|
Navigate |
Value (Vector2) | UI 导航方向 |
Submit |
Button | 确认 |
Cancel |
Button | 返回 |
Point |
Value (Vector2) | 鼠标/触摸位置(UI 点击) |
重映射配置
- 使用
PlayerInput组件的SaveBindingOverridesAsJson()持久化重映射到 PlayerPrefs - 启动时调用
LoadBindingOverridesFromJson()恢复
4. InputBuffer — 输入缓冲
InputBuffer 是一个轻量计时器组件,解决"输入早于判断条件成立"问题:
缓冲时长配置
| 输入动作 | 缓冲时长 | 说明 |
|---|---|---|
| 跳跃 | 0.15s | 落地前提前按跳跃,落地即起跳 |
| 攻击 | 0.12s | 前一段攻击结束前输入,自动接续下一击 |
| 弹反 | 0.0s | 不缓冲(弹反必须精准,缓冲会降低挑战性) |
| 冲刺 | 0.1s | 小量缓冲,避免帧率不稳定导致失手 |
| UseSpring | 0.0s | 不缓冲(消耗资源,防误操作) |
| SoulSkill | 0.1s | 小量缓冲,允许攻击后衔接技能 |
| SpiritSkill1/2 | 0.0s | 不缓冲(蓄力型技能按下即生效) |
缓冲工作原理
玩家按下跳跃键
→ InputBuffer 记录 jumpBufferTimer = 0.15s
→ 每帧 jumpBufferTimer -= deltaTime
↓
PlayerAirState 进入(落地)
→ 查询 InputBuffer.HasBufferedJump()
├── 若 jumpBufferTimer > 0 → true → 立即起跳 → 消费缓冲
└── 若 jumpBufferTimer ≤ 0 → false → 不起跳
缓冲接口
InputBuffer
├── bool HasBufferedJump() → 消费性查询(调用后清除)
├── bool HasBufferedAttack() → 消费性查询
├── bool HasBufferedDash() → 消费性查询
├── void ConsumeJump() → 手动消费
├── void ConsumeAttack()
└── void ConsumeDash()
5. Coyote Time
Coyote Time 让玩家在走出平台边缘后的短暂时间内仍可起跳,提升平台跳跃手感:
玩家离开地面
→ PlayerAirState 记录 coyoteTimer = 0.12s(当离地原因是走下而非跳跃时)
→ 每帧 coyoteTimer -= deltaTime
↓
玩家按下跳跃键(此时仍在 coyoteTimer > 0)
→ 视为地面跳跃(速度叠加正常跳跃力)
→ 消耗 coyoteTimer(不可再次触发)
Coyote Time 不生效的情况:
- 玩家主动跳跃后进入空中(不是走落)
- 已经触发过一次 Coyote Jump
- 玩家正在执行冲刺(DashState 期间禁用 Coyote)
| 参数 | 值 | 位置 |
|---|---|---|
CoyoteTimeDuration |
0.12s | PlayerMovementConfigSO |
JumpBufferDuration |
0.15s | PlayerMovementConfigSO |
6. 输入数据流
硬件设备(键盘/手柄)
↓
Input System Runtime
↓
PlayerInputActions(生成类,内嵌在 InputReaderSO)
↓
InputReaderSO.OnJumpPerformed() → 触发 JumpStartedEvent
↓
InputBuffer.RecordJump(timestamp)
↓
PlayerAirState.OnStateUpdate()
└── InputBuffer.HasBufferedJump() → true → 起跳
Action Map 切换时序:
游戏启动 → EnableGameplayInput()
打开暂停菜单 → EnableUIInput() (游戏逻辑冻结,输入切换到 UI)
关闭暂停菜单 → EnableGameplayInput()
进入过场动画 → DisableAllInput()
过场动画结束 → EnableGameplayInput()
7. 设备切换与多平台支持
InputReaderSO 监听 InputSystem.onActionChange,检测当前活跃设备类型,发布 DeviceChangedEvent(DeviceType),UI 系统根据此事件切换图标(键盘图标 / 手柄图标)。
| DeviceType | 说明 |
|---|---|
KeyboardMouse |
键盘 + 鼠标 |
Gamepad |
手柄(PS / Xbox) |
Touch |
触屏(移动端扩展,P2) |
8. 编辑器友好设计
InputReaderSO Inspector
自定义 Inspector(BaseGames.Editor)提供:
- 实时事件监控(Play Mode):每个 Action 上方显示"上次触发时间"
- 手动触发按钮:在 Inspector 中点击"Simulate Jump"等按钮,无需按实体键,方便调试 FSM 状态
Input 配置可视化
InputBuffer 在 Inspector 中以进度条显示各缓冲的剩余时间(只读),方便调试缓冲窗口:
┌─ InputBuffer ──────────────────────────────┐
│ Jump Buffer [████░░░░░░░] 0.08s / 0.15s │
│ Attack Buffer [░░░░░░░░░░░] 0.00s / 0.12s │
│ Dash Buffer [░░░░░░░░░░░] 0.00s / 0.10s │
└────────────────────────────────────────────┘