reckfellENGINE

Контроллеры: действия и мёртвые зоны

WASD и стик через одни действия без потери аналоговой величины.

На этой странице

Для движения задайте действия Input Map, а затем читайте результат независимо от физической кнопки. Прямой опрос номера оси нужен для диагностики или отдельной схемы управления, а не вместо всех игровых действий.

Какой метод выбрать

ЗадачаAPI
Ходьба по двум осям со стика или клавишInput.GetVector(left, right, up, down).
Одно направление с отрицательным и положительным значениемInput.GetAxis(negative, positive).
Аналоговая величина одного действияInput.GetActionStrength(action).
Удержание кнопкиInput.IsActionPressed(action).
Одно действие при нажатииInput.IsActionJustPressed(action).
Проверить необработанное значение физической осиInput.GetJoyAxis(device, axis).

Движение мыши и отклонение стика не имеют одинаковых единиц. Мышь сообщает приращение за событие, стик обычно задаёт направление и интенсивность, из которых вы вычисляете скорость поворота. Не применяйте к ним одну формулу с одинаковым delta.

Назначьте устройство

Input.GetConnectedJoypads() возвращает текущие идентификаторы устройств. Не считайте, что первый подключённый контроллер всегда имеет ID 0 или что его ID — постоянный идентификатор игрока. Храните назначение игрок → устройство и обновляйте его по Input.JoyConnectionChanged; после отключения предложите новое назначение и остановите зависящие от него действия.

Событие Input Map может быть привязано к конкретному устройству или ко всем устройствам. В локальной игре на нескольких контроллерах общая маска устройств не разделяет игроков. В примере ниже выбрано одно устройство; клавиатурные привязки общие и сами по себе не создают многопользовательский ввод.

Готовая обвязка для WASD и левого стика

Этот файл создаёт четыре новых действия для выбранного устройства, читает круговую мёртвую зону и удаляет созданные действия через Dispose(). Существующие действия с таким префиксом отклоняются, а не перезаписываются.

C#
using System;
using Reckfell;

public sealed class MovementActions : IDisposable
{
    private readonly string[] _names;
    private bool _disposed;
    public MovementActions(string prefix, int device)
    {
        if (string.IsNullOrWhiteSpace(prefix) || device < 0) throw new ArgumentOutOfRangeException();
        _names = new[] { prefix + "_left", prefix + "_right", prefix + "_up", prefix + "_down" };
        foreach (string name in _names)
            if (InputMap.HasAction(name)) throw new InvalidOperationException("Action already exists: " + name);
        Bind(_names[0], Key.A, JoyAxis.LeftX, -1, device);
        Bind(_names[1], Key.D, JoyAxis.LeftX, 1, device);
        Bind(_names[2], Key.W, JoyAxis.LeftY, -1, device);
        Bind(_names[3], Key.S, JoyAxis.LeftY, 1, device);
    }
    private static void Bind(string name, Key key, JoyAxis axis, float sign, int device)
    {
        InputMap.AddAction(name, 0.2f);
        InputMap.ActionAddEvent(name, new InputEventKey { PhysicalKeycode = key });
        InputMap.ActionAddEvent(name, new InputEventJoypadMotion { Device = device, Axis = axis, AxisValue = sign });
    }
    public Vector2 Read(float deadzone = 0.2f)
    {
        if (_disposed) throw new ObjectDisposedException(nameof(MovementActions));
        if (!float.IsFinite(deadzone) || deadzone < 0 || deadzone >= 1) throw new ArgumentOutOfRangeException(nameof(deadzone));
        return Input.GetVector(_names[0], _names[1], _names[2], _names[3], deadzone);
    }
    public void Dispose()
    {
        if (_disposed) return;
        foreach (string name in _names) InputMap.EraseAction(name);
        _disposed = true;
    }
}

Создайте MovementActions один раз, например в _Ready(), с уникальным префиксом и ID выбранного подключённого устройства. В физическом цикле читайте Read() и задавайте CharacterBody2D.Velocity = direction * speed, затем вызывайте MoveAndSlide(). Не создавайте набор действий каждый кадр. Освобождайте его при окончательном завершении использования; после Dispose() чтение запрещено. Привязки, добавленные кодом, не становятся автоматически сохранёнными настройками проекта.

Для собственного Input Map используйте те же четыре направления. Сохраняйте небольшое аналоговое отклонение: безусловный Normalized() после GetVector() превратит любую ненулевую величину в полную скорость.

Мёртвая зона: точный смысл

GetVector() берёт необработанные силы четырёх действий, строит вектор и применяет круговую мёртвую зону к его длине. За зоной величина перенормируется к диапазону 0–1; диагональ ограничивается длиной 1. Если параметр отрицательный, используется среднее значение мёртвых зон четырёх действий. Это отличается от двух независимых GetAxis().

В выполненной проверке с зоной 0,2:

  • Отклонение X=0,1 дало ноль, хотя raw-сила оставалась 0,1.
  • X=0,6 дало X=0,5 после перенормировки.
  • (0,3; 0,4) дало (0,225; 0,3): длина уменьшилась с 0,5 до 0,375.
  • (1; 1) ограничилось единичной длиной; WASD использует те же действия.

Не подбирайте порог по этим числам вслепую. В текущем InputMap.AddAction() значение по умолчанию 0,2; уже сохранённое действие может иметь другое значение. Настраивайте его под реальное устройство и оставляйте пользователю регулировку.

Если ввод неправильный

Нет контроллера в списке — сначала проверьте подключение на уровне ОС. Устройство есть, но действие не меняется — проверьте ID, ось, знак и привязку Input Map. Другой игрок двигает персонажа — проверьте фильтр устройства. Персонаж ползёт без касания — измерьте raw-отклонение и настройте мёртвую зону. При отпускании движение остаётся — проверьте обновление состояния и собственный накопленный вектор.

Кнопка геймпада не обязана давать клавиатурные echo-события: повтор меню реализуйте таймером с начальной задержкой. Для перепривязки используйте ввод и переназначение; для маршрута события через интерфейс — InputEvent.

Выполнены синтетические события осей и клавиш, круговая зона, диагональ, фильтр устройства и очистка. Физические контроллеры, вибрация, датчики, отключение устройства, фокус ОС и другие платформы здесь не проверялись. Наличие API не гарантирует поддержку функции любым устройством.

Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.