示例 · 输入组件化 Input
代码构造示例 InputActionAsset(UI / Sense 两个 map)→ 生成 ECS 组件 → A0 写入玩家实体 → B9 读取打印,不依赖手写 .inputactions 文件也能编译演示。
文件:SampleInputAssetBuilder.cs / SampleInputPlayerSpawner.cs / SampleInputReaderSystem.cs / UI/UIInputComponent.cs + Sense/SenseInputComponent.cs(生成器产物,已预生成)
心智模型
.inputactions/ InputActionAsset 是输入源:定义 map + action + 控制类型。InputComponentMapping(SO):把「map 名 → 输出文件夹 + 命名空间 + 组件名」登记下来。InputComponentGenerator按 map 生成<Map>InputComponent : IComponentData(富状态字段)+<Map>InputComponentWriter : IInputMapWriter([RuntimeInitializeOnLoadMethod]自注册进InputMapWriterRegistry)。InputComponentDriverSystem(A0) 每帧遍历玩家 × 所有 Writer,把当前输入写进玩家实体。- 宿主侧:建玩家实体 →
XxxInputComponent.AddTo(em, player)→InputPlayerRegistry.Register(player, 该玩家资产实例)→ 直接读组件。
关键代码
// 引导 / 玩家加入时(游戏层)
var player = em.CreateEntity();
UIInputComponent.AddTo(em, player); // 便捷挂载(生成器产出)
SenseInputComponent.AddTo(em, player);
// 设备配对由 InputUser 在游戏层完成;把"该玩家自己的" InputActionAsset 实例交给框架
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(); // 本帧刚按下
}
示例资产 / 组件字段对照
| map | action | 类型 | 生成组件字段 |
|---|---|---|---|
UI | Submit | Button | UIInputComponent.Submit (InputButton) |
UI | NavMove | Vector2 | UIInputComponent.NavMove (InputVector2) |
UI | Scroll | Axis | UIInputComponent.Scroll (InputAxis) |
Sense | Look | Vector2 | SenseInputComponent.Look (InputVector2) |
Sense | Fire | Button | SenseInputComponent.Fire (InputButton) |
绑定(演示用):Submit←Enter/Gamepad.South,NavMove←W/↑/左摇杆,Scroll←Mouse.scroll,Look←Mouse.delta/右摇杆,Fire←Space/Gamepad.West。
给 AI 的雷区
- 每名玩家要独立
InputActionAsset实例:SampleInputPlayerSpawner用Object.Instantiate(...)克隆。共享同一实例会让边沿(justPressed)与启用状态跨玩家串味。 UI/Sense组件与 Writer 是生成器产物(文件头标auto-generated):手改会被下次生成覆盖。AddTo是生成器随组件一起产出的静态扩展方法,不是你手写的。- 读取必须晚于 A0 写入:示例 reader 挂在
UpdateGroup_B9(晚于 A0);UI 组天然晚于 A0,安全。RegisterHandler<TComp>的回调由驱动系统在 A0 写完本玩家组件后即时触发。 UI/与Sense/各自独立 asmdef(HYC_Simple_Input_UI/HYC_Simple_Input_Sense):两包互不可见,只引用HYC.Framework.Runtime,靠InputMapWriterRegistry接口契约解耦——这就是「按 map 分包」的含义。- Writer 靠
[RuntimeInitializeOnLoadMethod]自注册:生成的 Writer 所在 asmdef 必须能被加载,否则驱动系统扫不到它、组件永远写不进。
验证清单
- 进 Play,Console 每 ~1 秒打印
[HYC.Simple.Input] 玩家 e=... Submit点亮=... NavMove=... Scroll=... Look=... Fire=...。 - 按 Enter / W / ↑ → 日志里
Submit/NavMove变化;按空格 →Fire点亮。 - 真实链路:Input System 编辑器建
.inputactions→ 建InputComponentMapping指向它 → 菜单Generate Input Components→ 玩家实体挂组件 +Register→ Play 后读组件。