[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"forum-post:6B2PshWj3nXQZbNnHBfi":3},{"post":4,"notFound":35,"comments":37,"commentPages":29},{"id":5,"title":6,"blocksByLocale":10,"availableLocales":21,"gameId":25,"category":26,"authorId":27,"authorName":28,"commentCount":29,"viewCount":30,"reactionCounts":31,"totalReactionCount":32,"myReaction":33,"pinScope":34,"isLocked":35,"isBookmarked":35,"createdAt":36,"updatedAt":36,"lastActivityAt":36},"6B2PshWj3nXQZbNnHBfi",{"zh-CN":7,"en-US":8,"ru-RU":9},"在 Lua Mod 中处理键盘输入","Handling Keyboard Input in Lua Mods","Обработка ввода с клавиатуры в Lua-модах",{"zh-CN":11,"en-US":15,"ru-RU":18},[12],{"type":13,"content":14},"text","> **TL;DR** —— `UnityEngine.Input.GetKey \u002F GetMouseButton` 已不再可用。请改用 `InputAPI.RegisterKeyInput` 来注册键盘热键。它基于事件驱动、跨平台，并能与游戏内的按键绑定 UI 集成。\n\n---\n\n## 为什么要改？\n\nPanzer War 已经迁移到 Unity 的新 **Input System** 包。任何调用旧版 `UnityEngine.Input` API 的 Lua 脚本都会抛出：\n\n```\nInvalidOperationException: You are trying to read Input using the UnityEngine.Input class,\nbut you have switched active Input handling to Input System package in Player Settings.\n```\n\n仍在使用 `Input.GetKey(...)`、`Input.GetMouseButton(...)`、`Input.GetAxis(...)` 等接口的 mod 都需要更新。\n\n---\n\n## 推荐方案：`InputAPI.RegisterKeyInput`\n\n`InputAPI` 是官方提供的 Lua 端输入封装。它为一个命名 action 和键码注册一对回调（按下 \u002F 抬起）。\n\n### 函数签名\n\n```lua\nInputAPI.RegisterKeyInput(actionName, keyCode, onPressed, onReleased)\nInputAPI.UnregisterKeyInput(actionName)\n```\n\n| 参数 | 类型 | 说明 |\n|---|---|---|\n| `actionName` | `string` | 该绑定的全局唯一 ID。请使用 mod 专属前缀避免冲突，例如 `\"MyMod_Reload\"`。 |\n| `keyCode` | `string` | Input System 的键盘键名（小写）。见下方对照表。 |\n| `onPressed` | `function(ctx)` | 按键按下时调用一次。 |\n| `onReleased` | `function(ctx)` | 按键抬起时调用一次。 |\n\n> **重要：** `actionName` 必须在**所有**已加载的 mod 中保持唯一。重复注册同名 action 会打印错误并静默失败。请始终用你的 mod 名做前缀。\n\n---\n\n## 快速示例 —— 最简形式\n\n```lua\n-- 按下 F 打印 \"fire\"，抬起 F 打印 \"stop\"\nInputAPI.RegisterKeyInput(\"MyMod_Fire\", \"f\",\n    function(ctx) print(\"fire\")  end,\n    function(ctx) print(\"stop\")  end)\n```\n\n就这么简单。不需要 `Update()` 轮询，也不需要逐帧检查。\n\n---\n\n## 推荐写法 —— 带清理逻辑的 Behavior 类\n\n只要不是一行代码能搞定的事，都建议把绑定包在 `Behavior` 类里，这样在 mod 卸载时能自动清理。**忘记反注册会导致绑定泄漏** —— 它会一直存活到游戏重启。\n\n```lua\nBehavior()\n\nProperty = {\n    keycode = {\n        type  = \"string\",\n        value = \"space\"   -- 可在 mod inspector 中编辑\n    }\n}\n\nlocal FireOnSpace = class(\"FireOnSpace\")\n\nfunction FireOnSpace:ctor()\n    -- 用 mod 名做前缀，避免与其他 mod 冲突\n    self.actionName = \"FireOnSpace_Trigger\"\nend\n\nfunction FireOnSpace:OnStarted()\n    -- 把方法绑到 self 上，这样后面才能用同一个引用反注册\n    self.onPressedCb  = function(ctx) self:OnPressed(ctx)  end\n    self.onReleasedCb = function(ctx) self:OnReleased(ctx) end\n\n    InputAPI.RegisterKeyInput(\n        self.actionName,\n        self.keycode,\n        self.onPressedCb,\n        self.onReleasedCb)\nend\n\nfunction FireOnSpace:OnDestroyed()\n    -- 务必反注册！这是\"幽灵绑定\"bug 的头号来源。\n    InputAPI.UnregisterKeyInput(self.actionName)\nend\n\nfunction FireOnSpace:OnPressed(ctx)\n    print(\"Space pressed\")\n    -- 在这里写你的开火逻辑\nend\n\nfunction FireOnSpace:OnReleased(ctx)\n    print(\"Space released\")\nend\n\nreturn FireOnSpace\n```\n\n要点：\n\n1. **把 `keycode` 定义为 Property**，这样用户可以从 mod inspector 重新绑定，而不用改 Lua。\n2. **先把回调存到 `self` 上**再注册。这样反注册时引用是稳定的。\n3. **务必在 `OnDestroyed` 中反注册**。框架**不会**自动清理绑定。\n\n---\n\n## 键码对照表\n\n`keyCode` 是一个字符串，对应 Unity Input System 的键盘绑定。API 内部会自动加上 `keyboard\u002F` 前缀，所以你只需要传键名。\n\n| 旧版 `KeyCode`             | 新版 `keyCode` 字符串 |\n|----------------------------|----------------------|\n| `Space`                    | `\"space\"`            |\n| `W` \u002F `A` \u002F `S` \u002F `D`      | `\"w\"` \u002F `\"a\"` \u002F `\"s\"` \u002F `\"d\"` |\n| `F1` … `F12`               | `\"f1\"` … `\"f12\"`     |\n| `LeftShift`                | `\"leftShift\"`        |\n| `LeftCtrl`                 | `\"leftCtrl\"`         |\n| `LeftAlt`                  | `\"leftAlt\"`          |\n| `Return` \u002F Enter           | `\"enter\"`            |\n| `Escape`                   | `\"escape\"`           |\n| `Tab`                      | `\"tab\"`              |\n| `1` … `9`、`0`             | `\"1\"` … `\"9\"`、`\"0\"` |\n| 小键盘 0–9                 | `\"numpad0\"` … `\"numpad9\"` |\n| 方向键                     | `\"upArrow\"` \u002F `\"downArrow\"` \u002F `\"leftArrow\"` \u002F `\"rightArrow\"` |\n| Backspace \u002F Delete         | `\"backspace\"` \u002F `\"delete\"` |\n\n完整列表请参考 Unity 的 [Input System 键盘文档](https:\u002F\u002Fdocs.unity3d.com\u002FPackages\u002Fcom.unity.inputsystem@1.7\u002Fapi\u002FUnityEngine.InputSystem.Key.html) —— `Key` 枚举里的任何值都可以用，转成首字母小写形式即可。\n\n---\n\n## `InputAPI.RegisterKeyInput` **不**支持什么\n\n| 需求 | 替代方案 |\n|---|---|\n| 鼠标按键（左键 \u002F 右键） | `CompatibleInputManager.Instance.isWeaponFire` \u002F `.isWeaponAim` |\n| 鼠标增量 \u002F 轴 | `CompatibleInputManager.Instance.groundMouseDelta` \u002F `.flightMouseDelta` |\n| 手柄按键 \u002F 摇杆 | `CS.UnityEngine.InputSystem.Gamepad.current.\u003Cbutton>.isPressed` |\n| 直接轮询 `Keyboard.current` 状态 | `CS.UnityEngine.InputSystem.Keyboard.current.\u003Ckey>.isPressed` |\n\n`InputAPI.RegisterKeyInput` 设计上**只处理键盘**——对于游戏动作类的鼠标输入（开火、瞄准），应该用 `CompatibleInputManager`，这样你的 mod 才能在移动端、手柄、Android 鼠标捕获等环境下保持兼容。\n\n---\n\n## 常见坑\n\n### 1. 忘记反注册\nmod 重载时绑定不会自动消失。热重载几次之后你会看到：\n```\nSame action name MyMod_Fire has been registered\n```\n请始终把 `RegisterKeyInput` 和 `UnregisterKeyInput` 配对，写在销毁生命周期里。\n\n### 2. mod 之间的 action 名冲突\n如果两个 mod 都注册 `\"Fire\"`，第二个会静默失败。**给每个 action 名加上你的 mod ID 前缀**：\n```lua\nself.actionName = \"AwesomeTankMod_Fire\"\n```\n\n### 3. 正确捕获 `self`\n下面这种写法是错的 —— 回调里的 `self` 是 `nil`：\n```lua\nInputAPI.RegisterKeyInput(\"X\", \"f\",\n    function(ctx) self:OnPressed(ctx) end, ...)  -- 这里 self === nil\n```\n正确做法是先把回调存到 `self` 上（参见上面的类示例），或者用显式闭包：\n```lua\nlocal me = self\nInputAPI.RegisterKeyInput(\"X\", \"f\",\n    function(ctx) me:OnPressed(ctx) end, ...)\n```\n\n### 4. 回调收到的是 `InputAction.CallbackContext`\n通常用不到它，但需要时它能提供时间和值信息：\n```lua\nfunction self:OnPressed(ctx)\n    -- ctx.time、ctx.duration、ctx:ReadValue(...) 等\nend\n```\n\n---\n\n## 迁移速查表\n\n| 旧代码 | 新代码 |\n|---|---|\n| `if Input.GetKeyDown(KeyCode.F) then ... end` | 注册 `onPressed` 回调 |\n| `if Input.GetKeyUp(KeyCode.F) then ... end` | 注册 `onReleased` 回调 |\n| `if Input.GetKey(KeyCode.F) then ... end` | 在 `onPressed` 里置一个 flag，在 `onReleased` 里清掉，在 tick 里检查 flag |\n| `if Input.GetMouseButton(0) then ... end` | `CompatibleInputManager.Instance.isWeaponFire:Get()` |\n| `if Input.GetMouseButton(1) then ... end` | `CompatibleInputManager.Instance.isWeaponAim:Get()` |\n\n### 示例：模拟 `GetKey`（按住状态）\n\n```lua\nfunction MyMod:OnStarted()\n    self.isHeld = false\n\n    self.onPressedCb  = function() self.isHeld = true  end\n    self.onReleasedCb = function() self.isHeld = false end\n\n    InputAPI.RegisterKeyInput(\"MyMod_Hold\", \"f\",\n        self.onPressedCb, self.onReleasedCb)\nend\n\nfunction MyMod:Tick()\n    if self.isHeld then\n        -- 只要 F 键按着，这里就每帧执行\n    end\nend\n```\n\n---\n\n## 参考\n\n- `frame\u002Fdefine\u002Fapi.lua` —— `InputAPI` 的 Lua 绑定\n- `frame\u002Fdefine\u002Fcsharp.lua`（第 1514–1575 行）—— 完整的方法签名\n\n如有疑问或遇到问题，欢迎在 Mod 社区频道发帖 —— 请附上完整的 Lua 代码片段和你使用的 actionName。",[16],{"type":13,"content":17},"> **TL;DR** — `UnityEngine.Input.GetKey \u002F GetMouseButton` no longer works. Use `InputAPI.RegisterKeyInput` for keyboard hotkeys instead. It's event-driven, cross-platform, and integrates with the in-game key-binding UI.\n\n---\n\n## Why the change?\n\nPanzer War has migrated to Unity's new **Input System** package. Any Lua script that calls the legacy `UnityEngine.Input` API will throw:\n\n```\nInvalidOperationException: You are trying to read Input using the UnityEngine.Input class,\nbut you have switched active Input handling to Input System package in Player Settings.\n```\n\nMods that still use `Input.GetKey(...)`, `Input.GetMouseButton(...)`, `Input.GetAxis(...)` etc. need to be updated.\n\n---\n\n## The recommended approach: `InputAPI.RegisterKeyInput`\n\n`InputAPI` is the official Lua-side input wrapper. It registers a callback pair (pressed \u002F released) for a named action and a key code.\n\n### Signature\n\n```lua\nInputAPI.RegisterKeyInput(actionName, keyCode, onPressed, onReleased)\nInputAPI.UnregisterKeyInput(actionName)\n```\n\n| Parameter | Type | Description |\n|---|---|---|\n| `actionName` | `string` | Globally unique ID for this binding. Use a mod-specific prefix to avoid collisions, e.g. `\"MyMod_Reload\"`. |\n| `keyCode` | `string` | Input System keyboard name (lowercase). See the table below. |\n| `onPressed` | `function(ctx)` | Called once when the key is pressed down. |\n| `onReleased` | `function(ctx)` | Called once when the key is released. |\n\n> **Important:** `actionName` must be unique across **all** active mods. Registering the same name twice prints an error and silently fails. Always prefix with your mod name.\n\n---\n\n## Quick example — simplest form\n\n```lua\n-- Press F to print \"fire\", release F to print \"stop\"\nInputAPI.RegisterKeyInput(\"MyMod_Fire\", \"f\",\n    function(ctx) print(\"fire\")  end,\n    function(ctx) print(\"stop\")  end)\n```\n\nThat's it. No `Update()` polling, no per-frame checks.\n\n---\n\n## Recommended pattern — Behavior class with cleanup\n\nFor anything beyond a one-liner, wrap your binding in a `Behavior` class so it gets cleaned up when the mod is unloaded. **Forgetting to unregister leaks the binding** — it stays active until the game restarts.\n\n```lua\nBehavior()\n\nProperty = {\n    keycode = {\n        type  = \"string\",\n        value = \"space\"   -- editable from the mod inspector\n    }\n}\n\nlocal FireOnSpace = class(\"FireOnSpace\")\n\nfunction FireOnSpace:ctor()\n    -- Prefix with mod name to avoid collisions with other mods\n    self.actionName = \"FireOnSpace_Trigger\"\nend\n\nfunction FireOnSpace:OnStarted()\n    -- Bind methods to self so we can unregister the exact same reference later\n    self.onPressedCb  = function(ctx) self:OnPressed(ctx)  end\n    self.onReleasedCb = function(ctx) self:OnReleased(ctx) end\n\n    InputAPI.RegisterKeyInput(\n        self.actionName,\n        self.keycode,\n        self.onPressedCb,\n        self.onReleasedCb)\nend\n\nfunction FireOnSpace:OnDestroyed()\n    -- Always unregister! This is the #1 source of \"ghost binding\" bugs.\n    InputAPI.UnregisterKeyInput(self.actionName)\nend\n\nfunction FireOnSpace:OnPressed(ctx)\n    print(\"Space pressed\")\n    -- your fire logic here\nend\n\nfunction FireOnSpace:OnReleased(ctx)\n    print(\"Space released\")\nend\n\nreturn FireOnSpace\n```\n\nKey points:\n\n1. **Define `keycode` as a Property** so the user can rebind it from the mod inspector without editing Lua.\n2. **Store callbacks on `self`** before registering. This keeps a stable reference for unregistering.\n3. **Always unregister in `OnDestroyed`**. The framework does **not** clean up bindings automatically.\n\n---\n\n## Key code reference\n\n`keyCode` is a string that maps to a Unity Input System keyboard binding. The API internally prefixes it with `keyboard\u002F`, so you only pass the key name.\n\n| Legacy `KeyCode`           | New `keyCode` string |\n|----------------------------|----------------------|\n| `Space`                    | `\"space\"`            |\n| `W` \u002F `A` \u002F `S` \u002F `D`      | `\"w\"` \u002F `\"a\"` \u002F `\"s\"` \u002F `\"d\"` |\n| `F1` … `F12`               | `\"f1\"` … `\"f12\"`     |\n| `LeftShift`                | `\"leftShift\"`        |\n| `LeftCtrl`                 | `\"leftCtrl\"`         |\n| `LeftAlt`                  | `\"leftAlt\"`          |\n| `Return` \u002F Enter           | `\"enter\"`            |\n| `Escape`                   | `\"escape\"`           |\n| `Tab`                      | `\"tab\"`              |\n| `1` … `9`, `0`             | `\"1\"` … `\"9\"`, `\"0\"` |\n| Numpad 0–9                 | `\"numpad0\"` … `\"numpad9\"` |\n| Arrows                     | `\"upArrow\"` \u002F `\"downArrow\"` \u002F `\"leftArrow\"` \u002F `\"rightArrow\"` |\n| Backspace \u002F Delete         | `\"backspace\"` \u002F `\"delete\"` |\n\nFor the full list see Unity's [Input System keyboard documentation](https:\u002F\u002Fdocs.unity3d.com\u002FPackages\u002Fcom.unity.inputsystem@1.7\u002Fapi\u002FUnityEngine.InputSystem.Key.html) — any value from the `Key` enum works in lowercase first-letter form.\n\n---\n\n## What `InputAPI.RegisterKeyInput` does **not** do\n\n| Need | Use instead |\n|---|---|\n| Mouse buttons (left\u002Fright click) | `CompatibleInputManager.Instance.isWeaponFire` \u002F `.isWeaponAim` |\n| Mouse delta \u002F axis | `CompatibleInputManager.Instance.groundMouseDelta` \u002F `.flightMouseDelta` |\n| Gamepad buttons \u002F sticks | `CS.UnityEngine.InputSystem.Gamepad.current.\u003Cbutton>.isPressed` |\n| Polling raw `Keyboard.current` state | `CS.UnityEngine.InputSystem.Keyboard.current.\u003Ckey>.isPressed` |\n\n`InputAPI.RegisterKeyInput` is **keyboard-only**, by design — for game-action mouse input (fire, aim) you should use `CompatibleInputManager` so your mod stays compatible with mobile, controller, and Android mouse capture.\n\n---\n\n## Common gotchas\n\n### 1. Forgetting to unregister\nThe binding survives a mod reload. After enough hot-reloads you'll see:\n```\nSame action name MyMod_Fire has been registered\n```\nAlways pair `RegisterKeyInput` with `UnregisterKeyInput` in your destruction lifecycle.\n\n### 2. Action name collisions across mods\nIf two mods both register `\"Fire\"`, the second one silently fails. **Prefix every action name with your mod ID**:\n```lua\nself.actionName = \"AwesomeTankMod_Fire\"\n```\n\n### 3. Capturing `self` correctly\nThis is wrong — `self` is `nil` inside the callback:\n```lua\nInputAPI.RegisterKeyInput(\"X\", \"f\",\n    function(ctx) self:OnPressed(ctx) end, ...)  -- self === nil here\n```\nAlways store the callback on `self` first (see the class example above), or use an explicit closure:\n```lua\nlocal me = self\nInputAPI.RegisterKeyInput(\"X\", \"f\",\n    function(ctx) me:OnPressed(ctx) end, ...)\n```\n\n### 4. The callback receives an `InputAction.CallbackContext`\nYou usually don't need it, but it carries timing and value info if you do:\n```lua\nfunction self:OnPressed(ctx)\n    -- ctx.time, ctx.duration, ctx:ReadValue(...) etc.\nend\n```\n\n---\n\n## Migration cheat sheet\n\n| Old code | New code |\n|---|---|\n| `if Input.GetKeyDown(KeyCode.F) then ... end` | Register `onPressed` callback |\n| `if Input.GetKeyUp(KeyCode.F) then ... end` | Register `onReleased` callback |\n| `if Input.GetKey(KeyCode.F) then ... end` | Set a flag in `onPressed`, clear it in `onReleased`, check the flag in your tick |\n| `if Input.GetMouseButton(0) then ... end` | `CompatibleInputManager.Instance.isWeaponFire:Get()` |\n| `if Input.GetMouseButton(1) then ... end` | `CompatibleInputManager.Instance.isWeaponAim:Get()` |\n\n### Example: emulating `GetKey` (held-down state)\n\n```lua\nfunction MyMod:OnStarted()\n    self.isHeld = false\n\n    self.onPressedCb  = function() self.isHeld = true  end\n    self.onReleasedCb = function() self.isHeld = false end\n\n    InputAPI.RegisterKeyInput(\"MyMod_Hold\", \"f\",\n        self.onPressedCb, self.onReleasedCb)\nend\n\nfunction MyMod:Tick()\n    if self.isHeld then\n        -- runs every frame while F is down\n    end\nend\n```\n\n---\n\n## See also\n\n- `frame\u002Fdefine\u002Fapi.lua` — Lua binding for `InputAPI`\n- `frame\u002Fdefine\u002Fcsharp.lua` (lines 1514–1575) — full method signatures\n\nIf you have questions or run into issues, post in the modding community channel — please include your full Lua snippet and the actionName you're using.",[19],{"type":13,"content":20},"> **TL;DR** — `UnityEngine.Input.GetKey \u002F GetMouseButton` больше не работает. Используйте `InputAPI.RegisterKeyInput` для горячих клавиш клавиатуры. Этот подход основан на событиях, кроссплатформенный и интегрируется со встроенным интерфейсом настройки клавиш.\n\n---\n\n## Почему изменение?\n\nPanzer War перешёл на новый пакет Unity **Input System**. Любой Lua-скрипт, использующий устаревший API `UnityEngine.Input`, выбросит исключение:\n\n```\nInvalidOperationException: You are trying to read Input using the UnityEngine.Input class,\nbut you have switched active Input handling to Input System package in Player Settings.\n```\n\nМоды, которые всё ещё используют `Input.GetKey(...)`, `Input.GetMouseButton(...)`, `Input.GetAxis(...)` и т.п., необходимо обновить.\n\n---\n\n## Рекомендуемый подход: `InputAPI.RegisterKeyInput`\n\n`InputAPI` — это официальная обёртка ввода со стороны Lua. Она регистрирует пару колбэков (нажатие \u002F отпускание) для именованного действия и кода клавиши.\n\n### Сигнатура\n\n```lua\nInputAPI.RegisterKeyInput(actionName, keyCode, onPressed, onReleased)\nInputAPI.UnregisterKeyInput(actionName)\n```\n\n| Параметр | Тип | Описание |\n|---|---|---|\n| `actionName` | `string` | Глобально уникальный ID для этой привязки. Используйте префикс, специфичный для мода, чтобы избежать коллизий, например `\"MyMod_Reload\"`. |\n| `keyCode` | `string` | Имя клавиши в Input System (в нижнем регистре). См. таблицу ниже. |\n| `onPressed` | `function(ctx)` | Вызывается один раз при нажатии клавиши. |\n| `onReleased` | `function(ctx)` | Вызывается один раз при отпускании клавиши. |\n\n> **Важно:** `actionName` должен быть уникальным среди **всех** активных модов. Повторная регистрация того же имени выводит ошибку и тихо завершается неудачей. Всегда добавляйте префикс с именем вашего мода.\n\n---\n\n## Быстрый пример — самая простая форма\n\n```lua\n-- Нажмите F для вывода \"fire\", отпустите F для вывода \"stop\"\nInputAPI.RegisterKeyInput(\"MyMod_Fire\", \"f\",\n    function(ctx) print(\"fire\")  end,\n    function(ctx) print(\"stop\")  end)\n```\n\nВот и всё. Никакого опроса в `Update()`, никаких проверок каждый кадр.\n\n---\n\n## Рекомендуемый паттерн — класс Behavior с очисткой\n\nДля всего, что сложнее однострочника, оборачивайте привязку в класс `Behavior`, чтобы она очищалась при выгрузке мода. **Забыли отменить регистрацию — привязка утекает** — она остаётся активной до перезапуска игры.\n\n```lua\nBehavior()\n\nProperty = {\n    keycode = {\n        type  = \"string\",\n        value = \"space\"   -- редактируется из инспектора мода\n    }\n}\n\nlocal FireOnSpace = class(\"FireOnSpace\")\n\nfunction FireOnSpace:ctor()\n    -- Префикс с именем мода во избежание коллизий с другими модами\n    self.actionName = \"FireOnSpace_Trigger\"\nend\n\nfunction FireOnSpace:OnStarted()\n    -- Привязываем методы к self, чтобы позже отменить регистрацию по той же ссылке\n    self.onPressedCb  = function(ctx) self:OnPressed(ctx)  end\n    self.onReleasedCb = function(ctx) self:OnReleased(ctx) end\n\n    InputAPI.RegisterKeyInput(\n        self.actionName,\n        self.keycode,\n        self.onPressedCb,\n        self.onReleasedCb)\nend\n\nfunction FireOnSpace:OnDestroyed()\n    -- Всегда отменяйте регистрацию! Это источник №1 багов \"призрачных привязок\".\n    InputAPI.UnregisterKeyInput(self.actionName)\nend\n\nfunction FireOnSpace:OnPressed(ctx)\n    print(\"Space pressed\")\n    -- здесь ваша логика выстрела\nend\n\nfunction FireOnSpace:OnReleased(ctx)\n    print(\"Space released\")\nend\n\nreturn FireOnSpace\n```\n\nКлючевые моменты:\n\n1. **Объявляйте `keycode` как Property**, чтобы пользователь мог переназначить клавишу из инспектора мода без правки Lua.\n2. **Сохраняйте колбэки в `self`** перед регистрацией. Это даёт стабильную ссылку для отмены регистрации.\n3. **Всегда отменяйте регистрацию в `OnDestroyed`**. Фреймворк **не** очищает привязки автоматически.\n\n---\n\n## Справочник кодов клавиш\n\n`keyCode` — это строка, отображаемая на привязку клавиатуры Unity Input System. API внутренне добавляет префикс `keyboard\u002F`, поэтому передавайте только имя клавиши.\n\n| Старый `KeyCode`           | Новая строка `keyCode` |\n|----------------------------|------------------------|\n| `Space`                    | `\"space\"`              |\n| `W` \u002F `A` \u002F `S` \u002F `D`      | `\"w\"` \u002F `\"a\"` \u002F `\"s\"` \u002F `\"d\"` |\n| `F1` … `F12`               | `\"f1\"` … `\"f12\"`       |\n| `LeftShift`                | `\"leftShift\"`          |\n| `LeftCtrl`                 | `\"leftCtrl\"`           |\n| `LeftAlt`                  | `\"leftAlt\"`            |\n| `Return` \u002F Enter           | `\"enter\"`              |\n| `Escape`                   | `\"escape\"`             |\n| `Tab`                      | `\"tab\"`                |\n| `1` … `9`, `0`             | `\"1\"` … `\"9\"`, `\"0\"`   |\n| Numpad 0–9                 | `\"numpad0\"` … `\"numpad9\"` |\n| Стрелки                    | `\"upArrow\"` \u002F `\"downArrow\"` \u002F `\"leftArrow\"` \u002F `\"rightArrow\"` |\n| Backspace \u002F Delete         | `\"backspace\"` \u002F `\"delete\"` |\n\nПолный список смотрите в [документации клавиатуры Input System Unity](https:\u002F\u002Fdocs.unity3d.com\u002FPackages\u002Fcom.unity.inputsystem@1.7\u002Fapi\u002FUnityEngine.InputSystem.Key.html) — подойдёт любое значение из перечисления `Key` в форме с маленькой первой буквой.\n\n---\n\n## Чего `InputAPI.RegisterKeyInput` **не** делает\n\n| Что нужно | Используйте вместо этого |\n|---|---|\n| Кнопки мыши (левая\u002Fправая) | `CompatibleInputManager.Instance.isWeaponFire` \u002F `.isWeaponAim` |\n| Дельта \u002F ось мыши | `CompatibleInputManager.Instance.groundMouseDelta` \u002F `.flightMouseDelta` |\n| Кнопки геймпада \u002F стики | `CS.UnityEngine.InputSystem.Gamepad.current.\u003Cbutton>.isPressed` |\n| Опрос состояния `Keyboard.current` | `CS.UnityEngine.InputSystem.Keyboard.current.\u003Ckey>.isPressed` |\n\n`InputAPI.RegisterKeyInput` работает **только с клавиатурой** — это сделано намеренно. Для игровых действий мыши (огонь, прицеливание) используйте `CompatibleInputManager`, чтобы ваш мод оставался совместимым с мобильными устройствами, контроллерами и захватом мыши на Android.\n\n---\n\n## Типичные подводные камни\n\n### 1. Забыли отменить регистрацию\nПривязка переживает перезагрузку мода. После нескольких горячих перезагрузок вы увидите:\n```\nSame action name MyMod_Fire has been registered\n```\nВсегда сочетайте `RegisterKeyInput` с `UnregisterKeyInput` в жизненном цикле уничтожения.\n\n### 2. Коллизии имён действий между модами\nЕсли два мода регистрируют `\"Fire\"`, второй тихо завершится неудачей. **Префиксуйте каждое имя действия ID вашего мода**:\n```lua\nself.actionName = \"AwesomeTankMod_Fire\"\n```\n\n### 3. Корректный захват `self`\nЭто неправильно — `self` равен `nil` внутри колбэка:\n```lua\nInputAPI.RegisterKeyInput(\"X\", \"f\",\n    function(ctx) self:OnPressed(ctx) end, ...)  -- self === nil здесь\n```\nВсегда сохраняйте колбэк в `self` сначала (см. пример класса выше) или используйте явное замыкание:\n```lua\nlocal me = self\nInputAPI.RegisterKeyInput(\"X\", \"f\",\n    function(ctx) me:OnPressed(ctx) end, ...)\n```\n\n### 4. Колбэк получает `InputAction.CallbackContext`\nОбычно он не нужен, но содержит информацию о времени и значениях, если потребуется:\n```lua\nfunction self:OnPressed(ctx)\n    -- ctx.time, ctx.duration, ctx:ReadValue(...) и т.д.\nend\n```\n\n---\n\n## Шпаргалка по миграции\n\n| Старый код | Новый код |\n|---|---|\n| `if Input.GetKeyDown(KeyCode.F) then ... end` | Зарегистрируйте колбэк `onPressed` |\n| `if Input.GetKeyUp(KeyCode.F) then ... end` | Зарегистрируйте колбэк `onReleased` |\n| `if Input.GetKey(KeyCode.F) then ... end` | Установите флаг в `onPressed`, сбросьте в `onReleased`, проверяйте флаг в тике |\n| `if Input.GetMouseButton(0) then ... end` | `CompatibleInputManager.Instance.isWeaponFire:Get()` |\n| `if Input.GetMouseButton(1) then ... end` | `CompatibleInputManager.Instance.isWeaponAim:Get()` |\n\n### Пример: эмуляция `GetKey` (состояние удержания)\n\n```lua\nfunction MyMod:OnStarted()\n    self.isHeld = false\n\n    self.onPressedCb  = function() self.isHeld = true  end\n    self.onReleasedCb = function() self.isHeld = false end\n\n    InputAPI.RegisterKeyInput(\"MyMod_Hold\", \"f\",\n        self.onPressedCb, self.onReleasedCb)\nend\n\nfunction MyMod:Tick()\n    if self.isHeld then\n        -- выполняется каждый кадр, пока F нажата\n    end\nend\n```\n\n---\n\n## См. также\n\n- `frame\u002Fdefine\u002Fapi.lua` — привязка Lua для `InputAPI`\n- `frame\u002Fdefine\u002Fcsharp.lua` (строки 1514–1575) — полные сигнатуры методов\n\nЕсли у вас есть вопросы или возникли проблемы, пишите в канал сообщества модмейкеров — пожалуйста, прикладывайте полный Lua-сниппет и используемое actionName.",[22,23,24],"zh-CN","en-US","ru-RU","mod-dev","general","5d8f0d1bf7ea721208f8b4b9","DevTeam",0,168,{"like":32,"love":29,"laugh":29,"wow":29,"sad":29},1,null,"none",false,"2026-04-25T05:20:02.276Z",[]]