Log in to join the discussion
Back to list

Обработка ввода с клавиатуры в Lua-модах

General
OPApr 25, 05:20 AM170

TL;DRUnityEngine.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

Ключевые моменты:

  1. Объявляйте keycode как Property, чтобы пользователь мог переназначить клавишу из инспектора мода без правки Lua.
  2. Сохраняйте колбэки в self перед регистрацией. Это даёт стабильную ссылку для отмены регистрации.
  3. Всегда отменяйте регистрацию в OnDestroyed. Фреймворк не очищает привязки автоматически.

Справочник кодов клавиш

keyCode — это строка, отображаемая на привязку клавиатуры Unity Input System. API внутренне добавляет префикс keyboard/, поэтому передавайте только имя клавиши.

Старый KeyCode Новая строка keyCode
Space "space"
W / A / S / D "w" / "a" / "s" / "d"
F1F12 "f1""f12"
LeftShift "leftShift"
LeftCtrl "leftCtrl"
LeftAlt "leftAlt"
Return / Enter "enter"
Escape "escape"
Tab "tab"
19, 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 для InputAPI
  • frame/define/csharp.lua (строки 1514–1575) — полные сигнатуры методов

Если у вас есть вопросы или возникли проблемы, пишите в канал сообщества модмейкеров — пожалуйста, прикладывайте полный Lua-сниппет и используемое actionName.

0 replies
1 / 1
1/200/2000
Login To Reply