◆ 持续开发中 · 当前为非稳定版本,如需抢先体验请通过 GitHub Issues 联系作者

示例 · 输入组件化 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();                  // 本帧刚按下
}

示例资产 / 组件字段对照

mapaction类型生成组件字段
UISubmitButtonUIInputComponent.Submit (InputButton)
UINavMoveVector2UIInputComponent.NavMove (InputVector2)
UIScrollAxisUIInputComponent.Scroll (InputAxis)
SenseLookVector2SenseInputComponent.Look (InputVector2)
SenseFireButtonSenseInputComponent.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 后读组件。