Начало работы с XInput в приложениях для Windows
XInput позволяет приложениям Windows обрабатывать взаимодействие контроллера (включая эффекты свораивания контроллера и входные и выходные данные).
В этом разделе представлен краткий обзор возможностей XInput и его настройка в приложении. Это включает следующее:
Общие сведения о XInput
Приложения могут использовать API XInput для взаимодействия с игровыми контроллерами при подключении к компьютеру с Windows (за раз можно подключить до четырех уникальных контроллеров).
С помощью этого API можно запросить любой совместимый подключенный контроллер, и можно задать эффекты вибрации. Контроллеры, подключенные к гарнитуре, также могут запрашиваться для звуковых и выходных устройств, которые можно использовать с гарнитурой для обработки голоса.
Макет контроллера
Совместимые контроллеры имеют два аналоговых указателя, каждый из которых имеет цифровую кнопку, два аналоговых триггера, цифровую панель направления с четырьмя направлениями и восемь цифровых кнопок. Состояния каждого из этих входных данных возвращаются в структуре XINPUT_GAMEPAD при вызове функции XInputGetState.
Контроллер также имеет два двигателя вибрации, чтобы обеспечить эффекты обратной связи пользователю. Скорость этих двигателей указывается в XINPUT_VIBRATION структуре, передаваемой функции XInputSetState, чтобы задать эффекты вибрации.
При необходимости гарнитура может быть подключена к контроллеру. Гарнитура имеет микрофон для голосового ввода и наушники для вывода звука. Вы можете вызвать функцию XInputGetAudioDeviceIds или устаревшую функцию XInputGetDoundAudioDeviceGuids, чтобы получить идентификаторы устройства, соответствующие устройствам для микрофона и наушника. Затем вы можете использовать API Core Audio для получения голосовых входных данных и отправки выходных данных звука.
Использование XInput
Использование XInput так же просто, как вызов функций XInput по мере необходимости. С помощью функций XInput можно получить состояние контроллера, получить идентификаторы звука гарнитуры и задать эффекты рухнуть контроллера.
Несколько контроллеров
API XInput поддерживает до четырех контроллеров, подключенных в любое время. Для всех функций XInput требуется параметр dwUserIndex , передаваемый для идентификации заданного или запрашиваемого контроллера. Этот идентификатор будет находиться в диапазоне от 0 до 3 и автоматически задается XInput. Номер соответствует порту, в который подключен контроллер, и не изменяется.
Каждый контроллер отображает идентификатор, который он использует путем освещения квадранта на "кольце света" в центре контроллера. Значение dwUserIndex, равное 0, соответствует левому верхнему квадранте; нумерация выполняется по кольцу по часовой стрелке.
Приложения должны поддерживать несколько контроллеров.
Получение состояния контроллера
На протяжении всего времени приложения состояние получения от контроллера, вероятно, будет выполняться чаще всего. От кадра к кадру в игровом приложении необходимо получить состояние и обновить сведения о игре, чтобы отразить изменения контроллера.
Чтобы получить состояние, используйте функцию XInputGetState :
DWORD dwResult;
for (DWORD i=0; i< XUSER_MAX_COUNT; i++ )
{
XINPUT_STATE state;
ZeroMemory( &state, sizeof(XINPUT_STATE) );
// Simply get the state of the controller from XInput.
dwResult = XInputGetState( i, &state );
if( dwResult == ERROR_SUCCESS )
{
// Controller is connected
}
else
{
// Controller is not connected
}
}
Обратите внимание, что возвращаемое значение XInputGetState можно использовать для определения подключения контроллера. Приложения должны определять структуру для хранения сведений о внутреннем контроллере; Эти сведения следует сравнить с результатами XInputGetState , чтобы определить, какие изменения, такие как нажатия кнопки или аналоговые разностные контроллеры, были сделаны в этом кадре. В приведенном выше примере g_Controllers представляет такую структуру.
После получения состояния в структуре XINPUT_STATE вы можете проверка его для изменений и получить конкретные сведения о состоянии контроллера.
Элемент dwPacketNumber структуры XINPUT_STATE можно использовать для проверка, если состояние контроллера изменилось с момента последнего вызова XInputGetState. Если dwPacketNumber не изменяется между двумя последовательными вызовами XInputGetState, то в состоянии не было изменений. Если оно отличается, приложение должно проверка член геймпадной структуры XINPUT_STATE, чтобы получить более подробные сведения о состоянии.
По соображениям производительности не вызывайте XInputGetState для "пустого" слота пользователя каждый кадр. Рекомендуется разместить проверка для новых контроллеров каждые несколько секунд.
Мертвая зона
Чтобы пользователи имели согласованный игровой процесс, ваша игра должна правильно реализовать мертвую зону. Мёртвая зона — это "перемещение" значений, сообщаемых контроллером, даже если аналоговые отпечатки не тронуты и центрированы. Существует также мертвая зона для 2 аналоговых триггеров.
Примечание.
Игры, использующие XInput, которые не фильтруют мертвую зону вообще будет испытывать плохой игровой процесс. Обратите внимание, что некоторые контроллеры более чувствительны, чем другие, поэтому мертвая зона может отличаться от единицы к единице. Рекомендуется протестировать игры с несколькими различными контроллерами в разных системах.
Приложения должны использовать "мертвые зоны" для аналоговых входных данных (триггеров, палок), чтобы указать, когда движение было сделано достаточно на палке или триггере, чтобы считаться допустимым.
Приложение должно проверка для мертвых зон и реагировать на них, как в следующем примере:
XINPUT_STATE state = g_Controllers[i].state;
float LX = state.Gamepad.sThumbLX;
float LY = state.Gamepad.sThumbLY;
//determine how far the controller is pushed
float magnitude = sqrt(LX*LX + LY*LY);
//determine the direction the controller is pushed
float normalizedLX = LX / magnitude;
float normalizedLY = LY / magnitude;
float normalizedMagnitude = 0;
//check if the controller is outside a circular dead zone
if (magnitude > INPUT_DEADZONE)
{
//clip the magnitude at its expected maximum value
if (magnitude > 32767) magnitude = 32767;
//adjust magnitude relative to the end of the dead zone
magnitude -= INPUT_DEADZONE;
//optionally normalize the magnitude with respect to its expected range
//giving a magnitude value of 0.0 to 1.0
normalizedMagnitude = magnitude / (32767 - INPUT_DEADZONE);
}
else //if the controller is in the deadzone zero out the magnitude
{
magnitude = 0.0;
normalizedMagnitude = 0.0;
}
//repeat for right thumb stick
В этом примере вычисляется вектор направления контроллера и насколько далеко вдоль вектора контроллер был отправлен. Это позволяет применять циклическую мертвую зону, просто проверка, если величина контроллера больше, чем значение мертвой зоны. Кроме того, код нормализует величину контроллера, которая затем может быть умножена на конкретный фактор игры, чтобы преобразовать положение контроллера в единицы, относящиеся к игре.
Обратите внимание, что вы можете определить собственные мертвые зоны для палок и триггеров (в любом месте от 0 до 65534) или использовать предоставленные взаимодоставки, определенные как XINPUT_GAMEPAD_LEFT_THU МБ_DEADZONE, XINPUT_GAMEPAD_RIGHT_THU МБ_DEADZONE и XINPUT_GAMEPAD_TRIGGER_THRESHOLD в XInput.h:
#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
После принудительного применения мертвая зона может оказаться полезной для масштабирования результирующего диапазона [0.0.1.0], плавающей запятой (как показано в примере выше), и при необходимости применить нелинейное преобразование.
Например, при управлении играми, может быть полезно кубировать результат, чтобы обеспечить лучшее чувство вождения автомобилей с помощью игровой панели, так как кубинг результат дает вам больше точности в более низких диапазонах, что желательно, так как игроки обычно применяют мягкую силу, чтобы получить тонкое движение или применить жесткие силы весь путь в одном направлении, чтобы получить rd ответ.
Настройка эффектов вибрации
Помимо получения состояния контроллера, вы также можете отправлять данные вибрации контроллеру, чтобы изменить обратную связь, предоставленную пользователю контроллера. Контроллер содержит два хворотных двигателя, которые можно самостоятельно контролировать путем передачи значений функции XInputSetState .
Скорость каждого двигателя можно указать с помощью значения WORD в структуре XINPUT_VIBRATION, передаваемой функции XInputSetState следующим образом:
XINPUT_VIBRATION vibration;
ZeroMemory( &vibration, sizeof(XINPUT_VIBRATION) );
vibration.wLeftMotorSpeed = 32000; // use any value between 0-65535 here
vibration.wRightMotorSpeed = 16000; // use any value between 0-65535 here
XInputSetState( i, &vibration );
Обратите внимание, что правый двигатель является высокочастотным двигателем, левый двигатель является низкочастотным двигателем. Они не всегда должны быть заданы одинаково, так как они обеспечивают различные эффекты.
Получение идентификаторов аудиоустройств
Гарнитура для контроллера имеет следующие функции:
- Запись звука с помощью микрофона
- Воспроизведение звука обратно с помощью наушника
Используйте этот код для получения идентификаторов устройства для гарнитуры:
WCHAR renderId[ 256 ] = {0};
WCHAR captureId[ 256 ] = {0};
UINT rcount = 256;
UINT ccount = 256;
XInputGetAudioDeviceIds( i, renderId, &rcount, captureId, &ccount );
После получения идентификаторов устройства можно создать соответствующие интерфейсы. Например, если вы используете XAudio 2.8, используйте этот код для создания эталонного голоса для этого устройства:
IXAudio2* pXAudio2 = NULL;
HRESULT hr;
if ( FAILED(hr = XAudio2Create( &pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR ) ) )
return hr;
IXAudio2MasteringVoice* pMasterVoice = NULL;
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, renderId, NULL, AudioCategory_Communications ) ) )
return hr;
Сведения об использовании идентификатора устройства captureId см. в разделе "Запись потока".
Получение идентификаторов GUID DirectSound (только устаревший пакет SDK DirectX)
Гарнитура, которая может быть подключена к контроллеру, имеет две функции: она может записывать звук с помощью микрофона, и он может воспроизводить звук с помощью наушники. В API XInput эти функции выполняются с помощью DirectSound с помощью интерфейсов IDirectSound8 и IDirectSoundCapture8.
Чтобы связать микрофон гарнитуры и наушники с соответствующими интерфейсами DirectSound, необходимо получить DirectSoundGUID для захвата и отрисовки устройств, вызвав XInputGetDSoundAudioDeviceGuids.
Примечание.
Использование устаревшего DirectSound не рекомендуется и недоступно в приложениях Магазина Windows. Сведения в этом разделе относятся только к версии XInput (XInput 1.3). В Windows 8 версии XInput (XInput 1.4) используются исключительно идентификаторы устройств API сеансов windows (WASAPI), полученные с помощью XInputGetAudioDeviceIds.
XInputGetDSoundAudioDeviceGuids( i, &dsRenderGuid, &dsCaptureGuid );
После получения идентификаторов GUID можно создать соответствующие интерфейсы, вызвав DirectSoundCreate8 и DirectSoundCaptureCreate8, как показано ниже:
// Create IDirectSound8 using the controller's render device
if( FAILED( hr = DirectSoundCreate8( &dsRenderGuid, &pDS, NULL ) ) )
return hr;
// Set coop level to DSSCL_PRIORITY
if( FAILED( hr = pDS->SetCooperativeLevel( hWnd, DSSCL_NORMAL ) ) )
return hr;
// Create IDirectSoundCapture using the controller's capture device
if( FAILED( hr = DirectSoundCaptureCreate8( &dsCaptureGuid, &pDSCapture, NULL ) ) )
return hr;