输入与热键
两条平行的输入通路,共用同一个 InputActionAsset:事件层(HotkeyManager,按动作名注册回调)+ 轮询层(输入组件化,按 map 生成 ECS 组件)。
命名空间 HYC.Framework.Input。
该用哪条
| 通路 | 入口 | 适用 |
|---|---|---|
| 热键(事件) | HotkeyManager + RegisterHotkey | 菜单 / 确认 / 取消 / 长按 / 带按键提示 UI |
| 组件化(轮询) | <Map>InputComponent + InputComponentDriverSystem | 摇杆 / 向量 / 轴 / 每帧读的玩法输入 |
一、热键 HotkeyManager
HotkeyManager 是静态注册表,把 InputSystem 的 Action 事件分发给多个回调,并按窗口焦点自动静默。框架不依赖游戏生成的枚举,一律用字符串动作名。
// 引导期(宿主做一次,必须先做)
HotkeyManager.BindAsset(myInputActionAsset);
private HotkeyHandle mHotkey;
public override void OnViewOpen(params object[] args)
{
// 最简单:动作名 + 回调
mHotkey = RegisterHotkey(HotkeyActionNames.UI_UGUI_Cancel, ctx => UIManager.Close<BagWindow>());
// 带长按(< 0.1 会被钳到 0.1,NaN 也变 0.1)
RegisterHotkey("Game/UseItem", OnUse, holdTime: 0.5f);
// 带 UI 提示元素(会在 parent 下生成按键提示 UI)
RegisterHotkey("Game/UseItem", someHotkeyElement, OnUse, priority: 10);
}
public override void OnViewClose()
{
UnregisterHotkey(mHotkey);
base.OnViewClose(); // 必调
}
完整签名:
public static HotkeyHandle RegisterHotkey(
string actionName,
Action<HotkeyInputContext> callback,
float holdTime = 0, // > 0 → Hold 模式
Transform parent = null, // 提示元素挂载点
string text = null, // 提示文本
HotkeyElementStyle style = HotkeyElementStyle.UI,
int priority = 0)
核心类型:HotkeyHandle(轻量 struct 句柄,只装一个 int ID)、HotkeyInputContext(统一回调载荷)、HotkeyInputPhase(Disabled/Waiting/Started/Performed/Canceled/End)、HotkeyMode(Press/Hold)、InputDevice、HotkeyActionNames、IHotkeyElement / BaseHotkeyElement。
热键的坑
- Press 模式一次按下回调多次,务必用
ctx.started/ctx.performed过滤。 priority不生效——源码里_sortedIDs从未被使用,分发顺序就是注册顺序。BindAsset重复调用会重复订阅InputSystem.onActionChange,图标刷新会跑多次。InputAsset == null时所有注册都失败(返回 ID=0 + LogError)——引导期必须先 BindAsset。UpdateAllHotkeyIcons()目前实际是空转;DisableTime只存不消费(给宿主 UI 做置灰倒计时)。
二、输入组件化
把 Input System 的 action 按 action map 动态生成为 ECS 的 IComponentData(富状态 struct),由驱动系统每帧把当前输入写进玩家实体;宿主在游戏逻辑里轮询式地「捞出来看」。
// 引导 / 玩家加入时(游戏层)
var player = em.CreateEntity();
UIInputComponent.AddTo(em, player); // 便捷挂载(生成器产出)
SenseInputComponent.AddTo(em, player);
InputPlayerRegistry.Register(player, myAssetInstance);
// 在某 System 里(例如挂在 UpdateGroup_B9 表现组)
if (SystemAPI.HasComponent<UIInputComponent>(player))
{
var ui = SystemAPI.GetComponent<UIInputComponent>(player);
if (ui.Submit.pressed) Confirm();
if (ui.NavMove.active) MoveCursor(ui.NavMove.value);
if (ui.Submit.justPressed) PlayClick(); // 本帧刚按下
}
也可注册 lambda 回调(A0 写完组件后即时触发):
InputPlayerRegistry.RegisterHandler<UIInputComponent>(player, ui =>
{
if (ui.Submit.justPressed) Confirm();
if (ui.Cancel.justPressed) CloseSelf();
if (ui.NavMove.active) DriveCursor(ui.NavMove.value);
});
菜单:HYC Framework/Input/Generate Input Components(扫描工程里所有 InputComponentMapping 资产并生成);Mapping 资产右键 → Generate This Mapping (Input Component)。
组件化的坑
- 每名玩家要独立
InputActionAsset实例——共享实例会让边沿与启用状态跨玩家串味。 - 生成代码设备无关:生成器只认 action 名与
expectedControlType,不碰任何具体设备;设备配对(InputUser)永远在游戏层。 - 组件字段按
expectedControlType定:action 没设控制类型会兜底成InputButton。要拿向量就得设成Vector2。 - 读取必须晚于 A0 写入。
◈
可运行的完整示例见 示例 · 输入组件化。