Что на самом деле делает «Калибровка игровых устройств» Windows?

Что на самом деле делает «Калибровка игровых устройств» Windows?

Что на самом деле делает этот мастер? Создает ли он мертвые зоны и сохраняет ли он их в реестре? Сопоставляет ли он необработанные данные с верхними и нижними границами где-то?

Это исключительно программная калибровка или она отправляет данные обратно на устройство?

Мне интересно, можно ли идентифицировать выходные данные этого мастера, чтобы сделать калибровку доступной из моего приложения.

решение1

В результате моего недавнего исследования встроенного в Windows инструмента калибровки контроллера джойстика я могу подтвердить следующее поведение джойстика PS4 (результаты должны быть аналогичными для других контроллеров):

  • После прохождения мастера калибровки записываются следующие регистры с откалиброванными данными, HKEY_CURRENT_USER\System\CurrentControlSet\Control\MediaProperties\PrivateProperties\DirectInput\<DEVICE_ID>\Calibration\0\Type\Axes\<NUM>где <NUM>— номер оси.

  • Для джойстика PS4 Windows считывает шесть осей, поэтому создаются шесть значений ключей реестра. Нумерация осей следующая:

    • 0 (x) --> Горизонтальное движение левого аналогового джойстика.
    • 1 (y) --> Вертикальное движение левого аналогового джойстика.
    • 2 (z) --> Правое аналоговое горизонтальное движение.
    • 3 (Rx) --> триггер L2.
    • 4 (Ry) --> триггер R2.
    • 5 (Rz) --> Правое аналоговое вертикальное движение.
  • Формат ключей реестра Calibrationотображается в 12-байтовое двоичное значение (закодированное как <MIN> <MID> <MAX>). Например, моя ось 0 (x) имеет значение: <00 00 00 00> <80 00 00 00> <ff 00 00 00>, что переводится как

    • Минимальное значение оси 0 = <00 00 00 00> = 0 (десятичное)
    • Значение средней оси 0 = <80 00 00 00> = 128 (десятичное)
    • Максимальное значение оси 0 <ff 00 00 00> = 255 (десятичное)
  • Калибровка, которую выполняет Windows, преобразует физические значения в калиброванный диапазон (минимальное, среднее, максимальное значения сверху). Например, предположим, что мой неисправный левый аналоговый джойстик показывает от 10 до 100 для горизонтального движения (ось x), когда он должен быть в диапазоне от 0 до 255. Тогда я могу установить минимальные/максимальные значения на 10 и 100 соответственно, чтобы откалибровать неисправный аналоговый джойстик.

  • Похоже, что для мертвых зон не существует специальной настройки, поэтому я предполагаю, что это деталь реализации, оставленная для приложения выше (например, для игры, определенная как часть кода игровой логики).

  • Поскольку настройки калибровки хранятся в реестре, калибровка не сохраняется на разных машинах.

Что касается вашего конкретного случая, вы можете рассмотреть возможность использования некоторых API Windows для чтения значений (например, XInput, UWP API). В качестве бонуса, вот некоторые из моих багги, которые считывают входные данные контроллера с помощью API Windows XInput.

#pragma comment(lib,"XInput.lib")
#pragma comment(lib,"Xinput9_1_0.lib")

#include <iostream>
#include <roapi.h>
#include <Xinput.h>


XINPUT_STATE fetchAConnectedJoystick() 
{
    DWORD dwResult;    
    XINPUT_STATE state;

    for (DWORD i=0; i < XUSER_MAX_COUNT; i++)
    {
        ZeroMemory(&state, sizeof(XINPUT_STATE));

        // Simply get the state of the controller from XInput.
        dwResult = XInputGetState(i, &state);

        // Controller is connected
        if(dwResult == ERROR_SUCCESS)
        {
            return state;
        }
    }

    std::exit;
}


void printLeftAnalogStickReadings(XINPUT_STATE state) 
{
    float LX = state.Gamepad.sThumbLX;
    float LY = state.Gamepad.sThumbLY;
    std::cout << "(X=" << LX << ", Y=" << LY << ")\n";
}


int computeAdjustedMagnitude(XINPUT_STATE state) 
{
    int INPUT_DEADZONE = 42;

    float LX = state.Gamepad.sThumbLX;
    float LY = state.Gamepad.sThumbLY;

    float magnitude = sqrt(LX*LX + LY*LY);              // Determine how far the controller is pushed

    // Determine the direction the controller is pushed
    float normalizedLX = LX / magnitude;
    float normalizedLY = LY / magnitude;

    if (magnitude > INPUT_DEADZONE)                     // Check if the controller is outside a circular dead zone
    {
        if (magnitude > 32767) magnitude = 32767;       // Clip the magnitude at its expected maximum value
        magnitude -= INPUT_DEADZONE;                    // Adjust magnitude relative to the end of the dead zone

    }
    else                                                // If the controller is in the deadzone zero out the magnitude
    {
        magnitude = 0.0;
    }
    return magnitude;
}


int main()
{
    XINPUT_STATE state;
    int sleepDuration = 100;                            // Milliseconds
    int adjustedMag = 0;

    while (true) {
        state = fetchAConnectedJoystick();
        printLeftAnalogStickReadings(state);
//      adjustedMag = computeAdjustedMagnitude(state);
//      std::this_thread::sleep_for(std::chrono::milliseconds(sleepDuration));
    }
}

решение2

Я полагаю, что информация о калибровке хранится в реестре по адресу:

HKEY_CURRENT_USER\System\CurrentControlSet\Control\MediaProperties\PrivateProperties\DirectInput\<DEVICE>\Calibration

Я не знаю, каков формат записей, но он нечитабельный.

Связанный контент