TL;DR —
UnityEngine.Input.GetKey / GetMouseButtonбольше не работает. ИспользуйтеInputAPI.RegisterKeyInputдля горячих клавиш клавиатуры. Этот подход основан на событиях, кроссплатформенный и интегрируется со встроенным интерфейсом настройки клавиш.
Почему изменение?
Panzer War перешёл на новый пакет Unity Input System. Любой Lua-скрипт, использующий устаревший API UnityEngine.Input, выбросит исключение:
InvalidOperationException: You are trying to read Input using the UnityEngine.Input class,
but you have switched active Input handling to Input System package in Player Settings.
Моды, которые всё ещё используют Input.GetKey(...), Input.GetMouseButton(...), Input.GetAxis(...) и т.п., необходимо обновить.
Рекомендуемый подход: InputAPI.RegisterKeyInput
InputAPI — это официальная обёртка ввода со стороны Lua. Она регистрирует пару колбэков (нажатие / отпускание) для именованного действия и кода клавиши.
Сигнатура
InputAPI.RegisterKeyInput(actionName, keyCode, onPressed, onReleased)
InputAPI.UnregisterKeyInput(actionName)
| Параметр | Тип | Описание |
|---|---|---|
actionName |
string |
Глобально уникальный ID для этой привязки. Используйте префикс, специфичный для мода, чтобы избежать коллизий, например "MyMod_Reload". |
keyCode |
string |
Имя клавиши в Input System (в нижнем регистре). См. таблицу ниже. |
onPressed |
function(ctx) |
Вызывается один раз при нажатии клавиши. |
onReleased |
function(ctx) |
Вызывается один раз при отпускании клавиши. |
Важно:
actionNameдолжен быть уникальным среди всех активных модов. Повторная регистрация того же имени выводит ошибку и тихо завершается неудачей. Всегда добавляйте префикс с именем вашего мода.
Быстрый пример — самая простая форма
-- Нажмите F для вывода "fire", отпустите F для вывода "stop"
InputAPI.RegisterKeyInput("MyMod_Fire", "f",
function(ctx) print("fire") end,
function(ctx) print("stop") end)
Вот и всё. Никакого опроса в Update(), никаких проверок каждый кадр.
Рекомендуемый паттерн — класс Behavior с очисткой
Для всего, что сложнее однострочника, оборачивайте привязку в класс Behavior, чтобы она очищалась при выгрузке мода. Забыли отменить регистрацию — привязка утекает — она остаётся активной до перезапуска игры.
Behavior()
Property = {
keycode = {
type = "string",
value = "space" -- редактируется из инспектора мода
}
}
local FireOnSpace = class("FireOnSpace")
function FireOnSpace:ctor()
-- Префикс с именем мода во избежание коллизий с другими модами
self.actionName = "FireOnSpace_Trigger"
end
function FireOnSpace:OnStarted()
-- Привязываем методы к self, чтобы позже отменить регистрацию по той же ссылке
self.onPressedCb = function(ctx) self:OnPressed(ctx) end
self.onReleasedCb = function(ctx) self:OnReleased(ctx) end
InputAPI.RegisterKeyInput(
self.actionName,
self.keycode,
self.onPressedCb,
self.onReleasedCb)
end
function FireOnSpace:OnDestroyed()
-- Всегда отменяйте регистрацию! Это источник №1 багов "призрачных привязок".
InputAPI.UnregisterKeyInput(self.actionName)
end
function FireOnSpace:OnPressed(ctx)
print("Space pressed")
-- здесь ваша логика выстрела
end
function FireOnSpace:OnReleased(ctx)
print("Space released")
end
return FireOnSpace
Ключевые моменты:
- Объявляйте
keycodeкак Property, чтобы пользователь мог переназначить клавишу из инспектора мода без правки Lua. - Сохраняйте колбэки в
selfперед регистрацией. Это даёт стабильную ссылку для отмены регистрации. - Всегда отменяйте регистрацию в
OnDestroyed. Фреймворк не очищает привязки автоматически.
Справочник кодов клавиш
keyCode — это строка, отображаемая на привязку клавиатуры Unity Input System. API внутренне добавляет префикс keyboard/, поэтому передавайте только имя клавиши.
Старый KeyCode |
Новая строка keyCode |
|---|---|
Space |
"space" |
W / A / S / D |
"w" / "a" / "s" / "d" |
F1 … F12 |
"f1" … "f12" |
LeftShift |
"leftShift" |
LeftCtrl |
"leftCtrl" |
LeftAlt |
"leftAlt" |
Return / Enter |
"enter" |
Escape |
"escape" |
Tab |
"tab" |
1 … 9, 0 |
"1" … "9", "0" |
| Numpad 0–9 | "numpad0" … "numpad9" |
| Стрелки | "upArrow" / "downArrow" / "leftArrow" / "rightArrow" |
| Backspace / Delete | "backspace" / "delete" |
Полный список смотрите в документации клавиатуры Input System Unity — подойдёт любое значение из перечисления Key в форме с маленькой первой буквой.
Чего InputAPI.RegisterKeyInput не делает
| Что нужно | Используйте вместо этого |
|---|---|
| Кнопки мыши (левая/правая) | CompatibleInputManager.Instance.isWeaponFire / .isWeaponAim |
| Дельта / ось мыши | CompatibleInputManager.Instance.groundMouseDelta / .flightMouseDelta |
| Кнопки геймпада / стики | CS.UnityEngine.InputSystem.Gamepad.current.<button>.isPressed |
Опрос состояния Keyboard.current |
CS.UnityEngine.InputSystem.Keyboard.current.<key>.isPressed |
InputAPI.RegisterKeyInput работает только с клавиатурой — это сделано намеренно. Для игровых действий мыши (огонь, прицеливание) используйте CompatibleInputManager, чтобы ваш мод оставался совместимым с мобильными устройствами, контроллерами и захватом мыши на Android.
Типичные подводные камни
1. Забыли отменить регистрацию
Привязка переживает перезагрузку мода. После нескольких горячих перезагрузок вы увидите:
Same action name MyMod_Fire has been registered
Всегда сочетайте RegisterKeyInput с UnregisterKeyInput в жизненном цикле уничтожения.
2. Коллизии имён действий между модами
Если два мода регистрируют "Fire", второй тихо завершится неудачей. Префиксуйте каждое имя действия ID вашего мода:
self.actionName = "AwesomeTankMod_Fire"
3. Корректный захват self
Это неправильно — self равен nil внутри колбэка:
InputAPI.RegisterKeyInput("X", "f",
function(ctx) self:OnPressed(ctx) end, ...) -- self === nil здесь
Всегда сохраняйте колбэк в self сначала (см. пример класса выше) или используйте явное замыкание:
local me = self
InputAPI.RegisterKeyInput("X", "f",
function(ctx) me:OnPressed(ctx) end, ...)
4. Колбэк получает InputAction.CallbackContext
Обычно он не нужен, но содержит информацию о времени и значениях, если потребуется:
function self:OnPressed(ctx)
-- ctx.time, ctx.duration, ctx:ReadValue(...) и т.д.
end
Шпаргалка по миграции
| Старый код | Новый код |
|---|---|
if Input.GetKeyDown(KeyCode.F) then ... end |
Зарегистрируйте колбэк onPressed |
if Input.GetKeyUp(KeyCode.F) then ... end |
Зарегистрируйте колбэк onReleased |
if Input.GetKey(KeyCode.F) then ... end |
Установите флаг в onPressed, сбросьте в onReleased, проверяйте флаг в тике |
if Input.GetMouseButton(0) then ... end |
CompatibleInputManager.Instance.isWeaponFire:Get() |
if Input.GetMouseButton(1) then ... end |
CompatibleInputManager.Instance.isWeaponAim:Get() |
Пример: эмуляция GetKey (состояние удержания)
function MyMod:OnStarted()
self.isHeld = false
self.onPressedCb = function() self.isHeld = true end
self.onReleasedCb = function() self.isHeld = false end
InputAPI.RegisterKeyInput("MyMod_Hold", "f",
self.onPressedCb, self.onReleasedCb)
end
function MyMod:Tick()
if self.isHeld then
-- выполняется каждый кадр, пока F нажата
end
end
См. также
frame/define/api.lua— привязка Lua дляInputAPIframe/define/csharp.lua(строки 1514–1575) — полные сигнатуры методов
Если у вас есть вопросы или возникли проблемы, пишите в канал сообщества модмейкеров — пожалуйста, прикладывайте полный Lua-сниппет и используемое actionName.