Исходное игровое устройство управления
На этой странице описываются основы программирования практически для любого типа игрового контроллера с помощью Windows.Gaming.Input.RawGameController и связанных API-интерфейсов для универсальная платформа Windows (UWP).
Прочитав эту страницу, вы узнаете:
- Как собрать список подключенных необработанных игровых контроллеров и их пользователей
- как определить, что необработанный игровой контроллер был добавлен или удален
- как получить возможности необработанного игрового контроллера
- чтение входных данных из необработанного игрового контроллера
Обзор
Необработанный игровой контроллер — это универсальное представление игрового контроллера с входными данными, найденными на различных типах общих игровых контроллеров. Эти входные данные предоставляются в виде простых массивов неименованных кнопок, коммутаторов и осей. С помощью необработанного игрового контроллера клиенты могут создавать пользовательские сопоставления входных данных независимо от типа используемого контроллера.
Класс RawGameController действительно предназначен для сценариев, когда другие входные классы (ArcadeStick, FlightStick и т. д.) не соответствуют вашим потребностям, если вы хотите что-то более универсальное, ожидая, что клиенты будут использовать множество различных типов игровых контроллеров, то этот класс предназначен для вас.
Обнаружение и отслеживание необработанных игровых контроллеров
Обнаружение и отслеживание необработанных игровых контроллеров работает точно так же, как и для геймпадов, за исключением класса RawGameController вместо класса Gamepad . Дополнительные сведения см. в разделе "Геймпад" и "Вибрация ".
Получение возможностей необработанного игрового контроллера
После идентификации необработанного игрового контроллера, который вас интересует, вы можете собирать сведения о возможностях контроллера. Вы можете получить количество кнопок на необработанном игровом контроллере с помощью RawGameController.ButtonCount, количества аналоговых осей с rawGameController.AxisCount и количества коммутаторов с rawGameController.SwitchCount. Кроме того, можно получить тип коммутатора с помощью RawGameController.GetSwitchKind.
В следующем примере возвращается количество входных данных необработанного игрового контроллера:
auto rawGameController = myRawGameControllers->GetAt(0);
int buttonCount = rawGameController->ButtonCount;
int axisCount = rawGameController->AxisCount;
int switchCount = rawGameController->SwitchCount;
В следующем примере определяется тип каждого коммутатора:
for (uint32_t i = 0; i < switchCount; i++)
{
GameControllerSwitchKind mySwitch = rawGameController->GetSwitchKind(i);
}
Чтение необработанного игрового контроллера
После того как вы знаете количество входных данных на необработанном игровом контроллере, вы готовы собрать входные данные из него. Однако, в отличие от некоторых других типов входных данных, которые вы можете использовать, необработанный игровой контроллер не взаимодействует с изменением состояния, вызывая события. Вместо этого вы принимаете регулярное чтение его текущего состояния, опросив его.
Опрос необработанного игрового контроллера
Опрос записывает моментальный снимок необработанного игрового контроллера в определенный момент времени. Такой подход к сбору входных данных подходит для большинства игр, так как их логика обычно выполняется в детерминированном цикле, а не на основе событий. Это также обычно проще интерпретировать команды игры из входных данных, собранных одновременно, чем из многих отдельных входных данных, собранных с течением времени.
Вы опросите необработанный игровой контроллер, вызвав RawGameController.GetCurrentReading. Эта функция заполняет массивы для кнопок, коммутаторов и осей, содержащих состояние необработанного игрового контроллера.
В следующем примере выполняется опрос необработанного игрового контроллера для текущего состояния:
Platform::Array<bool>^ currentButtonReading =
ref new Platform::Array<bool>(buttonCount);
Platform::Array<GameControllerSwitchPosition>^ currentSwitchReading =
ref new Platform::Array<GameControllerSwitchPosition>(switchCount);
Platform::Array<double>^ currentAxisReading = ref new Platform::Array<double>(axisCount);
rawGameController->GetCurrentReading(
currentButtonReading,
currentSwitchReading,
currentAxisReading);
Нет гарантии того, какая позиция в каждом массиве будет содержать входное значение между различными типами контроллеров, поэтому вам потребуется проверить, какие входные данные используют методы RawGameController.GetButtonLabel и RawGameController.GetSwitchKind.
GetButtonLabel сообщает вам текст или символ, напечатанный на физической кнопке, а не функцию кнопки, поэтому лучше всего использовать в качестве помощи в пользовательском интерфейсе в тех случаях, когда вы хотите дать подсказки проигрывателя о том, какие кнопки выполняют какие функции. GetSwitchKind сообщает тип переключателя (т. е. сколько позиций он имеет), но не имя.
Нет стандартизованного способа получить метку оси или коммутатора, поэтому вам потребуется протестировать эти данные самостоятельно, чтобы определить, какие входные данные являются.
Если у вас есть определенный контроллер, который вы хотите поддерживать, вы можете получить RawGameController.HardwareProductId и RawGameController.HardwareVendorId и проверить, совпадают ли они с этим контроллером. Позиция каждого входного элемента в каждом массиве одинакова для каждого контроллера с одинаковым HardwareProductId и HardwareVendorId, поэтому вам не нужно беспокоиться о потенциально несогласованности между различными контроллерами одного типа.
Помимо состояния необработанного игрового контроллера, каждое чтение возвращает метку времени, указывающую точное время извлечения состояния. Метка времени полезна для связи с временем предыдущих чтений или временем имитации игры.
Чтение кнопок и коммутаторов
Каждая из кнопок необработанного игрового контроллера обеспечивает цифровое чтение, указывающее, нажимается ли оно (вниз) или освобождается (вверх). Операции чтения кнопок представлены в виде отдельных логических значений в одном массиве. Метка для каждой кнопки можно найти с помощью RawGameController.GetButtonLabel с индексом логического значения в массиве. Каждое значение представлено как GameControllerButtonLabel.
В следующем примере определяется, нажимается ли кнопка XboxA :
for (uint32_t i = 0; i < buttonCount; i++)
{
if (currentButtonReading[i])
{
GameControllerButtonLabel buttonLabel = rawGameController->GetButtonLabel(i);
if (buttonLabel == GameControllerButtonLabel::XboxA)
{
// XboxA is pressed.
}
}
}
Иногда может потребоваться определить, когда кнопка переходит от нажатия на нажатие или освобождено на нажатие, если несколько кнопок нажимаются или освобождаются, или если набор кнопок упорядочивается определенным образом, некоторые нажимаются, а не. Сведения об обнаружении каждого из этих условий см. в разделе "Обнаружение переходов кнопок" и "Обнаружение сложных договоренностей кнопок".
Значения коммутатора предоставляются в виде массива GameControllerSwitchPosition. Так как это свойство представляет собой битовое поле, побитовое маскирование используется для изоляции направления коммутатора.
В следующем примере определяется, находится ли каждый переключатель в положении вверх:
for (uint32_t i = 0; i < switchCount; i++)
{
if (GameControllerSwitchPosition::Up ==
(currentSwitchReading[i] & GameControllerSwitchPosition::Up))
{
// The switch is in the up position.
}
}
Чтение аналоговых входных данных (палки, триггеры, регулирование и т. д.)
Аналоговая ось обеспечивает чтение от 0,0 до 1.0. Это включает в себя каждое измерение на палке, например X и Y для стандартных палочек или даже X, Y, и Z осей (рулон, шаг и рывок соответственно) для пролетных палок.
Значения могут представлять аналоговые триггеры, регулирование или любой другой тип аналоговых входных данных. Эти значения не предоставляются метками, поэтому мы рекомендуем проверить код с различными устройствами ввода, чтобы убедиться, что они соответствуют вашим предположениям.
Во всех осях значение приблизительно 0,5 для палки, когда она находится в центре, но это нормально для точного значения, даже между последующими считываниями; Стратегии устранения этого варианта рассматриваются далее в этом разделе.
В следующем примере показано, как считывать аналоговые значения из контроллера Xbox:
// Xbox controllers have 6 axes: 2 for each stick and one for each trigger.
float leftStickX = currentAxisReading[0];
float leftStickY = currentAxisReading[1];
float rightStickX = currentAxisReading[2];
float rightStickY = currentAxisReading[3];
float leftTrigger = currentAxisReading[4];
float rightTrigger = currentAxisReading[5];
При чтении значений палки вы заметите, что они не надежно создают нейтральное чтение 0,5 при хранении в положении центра; Вместо этого они будут создавать разные значения около 0,5 каждый раз, когда палка перемещается и возвращается в положение центра. Чтобы устранить эти варианты, можно реализовать небольшую мертвую зону, которая представляет собой диапазон значений вблизи идеального центрального положения, которые игнорируются.
Один из способов реализовать мертвую зону заключается в том, чтобы определить, насколько далеко палка двигалась с центра, и игнорировать чтения, которые ближе, чем какое-то расстояние, которое вы выбираете. Вы можете вычислить расстояние примерно — это не точно, потому что чтение палки по сути полярные, а не планарные, значения — просто с помощью теоремы Pythagorean. Это создает радиальную мертвую зону.
В следующем примере показана базовая радиальная мертвая зона с помощью теоремы Pythagorean:
// Choose a deadzone. Readings inside this radius are ignored.
const float deadzoneRadius = 0.1f;
const float deadzoneSquared = deadzoneRadius * deadzoneRadius;
// Pythagorean theorem: For a right triangle, hypotenuse^2 = (opposite side)^2 + (adjacent side)^2
float oppositeSquared = leftStickY * leftStickY;
float adjacentSquared = leftStickX * leftStickX;
// Accept and process input if true; otherwise, reject and ignore it.
if ((oppositeSquared + adjacentSquared) < deadzoneSquared)
{
// Input accepted, process it.
}