Поделиться через


Ручка управления полетом

На этой странице описаны основы программирования для сертифицированных на Xbox One рулей управления, используя Windows.Gaming.Input.FlightStick и связанные API для универсальной платформы Windows (UWP).

Прочитав эту страницу, вы узнаете:

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

Обзор

Джойстики — это игровые устройства ввода, которые ценятся за воспроизведение ощущения штурвалов, которые можно найти в кабине самолета или космического корабля. Это идеальное устройство ввода для быстрого и точного управления полетом. Джойстики поддерживаются в приложениях Windows 10 или Windows 11 и Xbox One через пространство имен Windows.Gaming.Input.

Сертифицированные Xbox One джойстики для авиасимуляторов оснащены следующими элементами управления:

  • Поворотный аналоговый джойстик, способный на крен, тангаж и рысканье.
  • Аналоговый дроссель
  • Две кнопки стрельбы
  • 8-направлений цифровой переключатель "шляпа"
  • кнопки Просмотр и Меню

Замечание

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

Навигация пользовательского интерфейса

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

В качестве контроллера навигации пользовательского интерфейса с помощью необходимый набор команд навигации сопоставляется с джойстиком и представлением, меню, FirePrimaryи кнопками FireSecondary.

Команда навигации Ввод с летного джойстика
Вверх Джойстик вверх
Вниз Джойстик вниз
Лево Джойстик слева
Правильно Джойстик вправо
Просмотреть Кнопка "Просмотреть"
Меню Меню , кнопка
Принять кнопка FirePrimary
Отменить Кнопка FireSecondary

Тримминг-рычаги не сопоставляют какой-либо из необязательных наборов команд навигации.

Обнаружение и отслеживание стиков полета

Обнаружение и отслеживание джойстиков для полетов работает точно так же, как и для геймпадов, за исключением того, что используется класс FlightStick вместо класса Gamepad. См. раздел «Геймпад и вибрация» для получения дополнительной информации.

Чтение пролетной палочки

После того как вы определите интересующий вас джойстик, вы готовы собирать данные с него. Тем не менее, в отличие от некоторых других типов входных данных, которые вы можете использовать, джойстики управления полетом не сообщают об изменении состояния через события. Вместо этого вы регулярно проверяете их текущее состояние путем опроса их.

Опрос пролетной палки

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

Вы проверяете состояние руля управления, вызовом FlightStick.GetCurrentReading. Эта функция возвращает FlightStickReading, содержащую состояние джойстика.

В следующем примере джойстик опрашивается для получения его текущего состояния.

auto flightStick = myFlightSticks->GetAt(0);
FlightStickReading reading = flightStick->GetCurrentReading();

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

Чтение входных данных джойстика и рычага управления

Джойстик обеспечивает аналоговое считывание в диапазоне от -1,0 до 1,0 по осям X, Y и Z (продольный крен, тангаж и рысканье соответственно). Для ролла значение -1.0 соответствует самой левой позиции джойстика, а значение 1.0 соответствует самой правой позиции. Для шага значение -1,0 соответствует нижней позиции джойстика, а значение 1,0 соответствует верхней позиции. Для рывка значение -1,0 соответствует самой витой позиции, в то время как значение 1,0 соответствует самой по часовой стрелке.

Во всех осях значение составляет примерно 0,0, когда джойстик находится в центральной позиции, но это нормально, когда точные значения изменяются даже при последующих измерениях. Стратегии устранения этого варианта рассматриваются далее в этом разделе.

Значение крена джойстика считывается из свойства FlightStickReading.Roll, значение тангажа считывается из свойства FlightStickReading.Pitch, а значение рысканья считывается из свойства FlightStickReading.Yaw:

// Each variable will contain a value between -1.0 and 1.0.
float roll = reading.Roll;
float pitch = reading.Pitch;
float yaw = reading.Yaw;

При чтении значений джойстика вы заметите, что они не надежно создают нейтральное чтение 0,0, когда джойстик находится в состоянии покоя в центре; Вместо этого они будут создавать разные значения около 0,0 каждый раз, когда джойстик перемещается и возвращается в положение центра. Чтобы уменьшить эти вариации, можно реализовать небольшую мёртвую зону, которая является диапазоном значений вблизи идеального центрального положения, которые игнорируются.

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

В следующем примере показана базовая радиальная мертвая зона с помощью теоремы 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 = pitch * pitch;
float adjacentSquared = roll * roll;

// Accept and process input if true; otherwise, reject and ignore it.
if ((oppositeSquared + adjacentSquared) < deadzoneSquared)
{
    // Input accepted, process it.
}

Чтение кнопок и переключателя шляпы

Каждая из двух кнопок огня на джойстике предоставляет цифровую индикацию состояния кнопки: нажата (вниз) или отпущена (вверх). Для повышения эффективности чтение кнопок не представлено отдельными логическими значениями. Вместо этого, все они упакованы в единое битовое поле, представленное перечислением FlightStickButtons. Кроме того, 8-позиционный переключатель предоставляет направление, упакованное в одно битовое поле, представленное перечислением GameControllerSwitchPosition.

Замечание

Рукоятки управления полётом оснащены дополнительными кнопками, используемыми для навигации пользовательского интерфейса, такими как кнопки Представление и Меню. Эти кнопки не являются частью перечисления FlightStickButtons и могут быть считаны только при обращении к рукоятке управления полетом в качестве устройства навигации пользовательского интерфейса. Дополнительные сведения см. в контроллере навигации пользовательского интерфейса.

Значения кнопки считываются из свойства FlightStickReading.Button . Так как это свойство представляет собой битовое поле, побитовое маскирование используется для изоляции значения нужной кнопки. Кнопка нажата (вниз), если установлен соответствующий бит; в противном случае она отпущена (вверх).

В следующем примере определяется, нажата ли кнопка FirePrimary:

if (FlightStickButtons::FirePrimary == (reading.Buttons & FlightStickButtons::FirePrimary))
{
    // FirePrimary is pressed.
}

В следующем примере определяется, выпущена ли кнопка FirePrimary:

if (FlightStickButtons::None == (reading.Buttons & FlightStickButtons::FirePrimary))
{
    // FirePrimary is released (not pressed).
}

Иногда может потребоваться определить, когда кнопка переходит от нажатия к освобождению или от освобождения к нажатию, нажаты или освобождены несколько кнопок, или если набор кнопок расположен определенным образом — некоторые нажаты, а некоторые нет. Сведения о том, как обнаружить каждое из этих условий, см. в разделах Обнаружение переходов кнопки и Обнаружение сложных схем расположения кнопок.

Значение параметра шляпы считывается из свойства FlightStickReading.HatSwitch. Так как это свойство также является битовым полем, побитовое маскирование снова используется для изоляции положения переключателя шляпы.

В следующем примере определяется, находится ли переключатель шляпы в положении вверх:

if (GameControllerSwitchPosition::Up == (reading.HatSwitch & GameControllerSwitchPosition::Up))
{
    // The hat switch is in the up position.
}

В следующем примере определяется, находится ли переключатель шляпы в центре.

if (GameControllerSwitchPosition::Center == (reading.HatSwitch & GameControllerSwitchPosition::Center))
{
    // The hat switch is in the center position.
}

См. также