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

9.2 KiB
Raw Permalink Blame History

01 · 输入系统

命名空间 BaseGames.Input
所属文档集 ← 返回索引 · 总览
依赖 Unity Input System · BaseGames.Core.Events


目录

  1. 设计目标
  2. 核心架构InputReaderSO
  3. Input Actions 资产结构
  4. InputBuffer — 输入缓冲
  5. Coyote Time
  6. 输入数据流
  7. 设备切换与多平台支持
  8. 编辑器友好设计

1. 设计目标

  • 零耦合:游戏系统不直接引用 PlayerInput 组件,通过 InputReaderSOScriptableObject订阅输入事件
  • 可测试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

自定义 InspectorBaseGames.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 │
└────────────────────────────────────────────┘