Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
XInput позволяет приложениям Windows обрабатывать взаимодействие контроллера (включая эффекты свораивания контроллера и входные и выходные данные).
В этом разделе представлен краткий обзор возможностей XInput и его настройка в приложении. Он включает в себя следующее:
Общие сведения о XInput
Приложения могут использовать API XInput для взаимодействия с игровыми контроллерами при подключении к компьютеру с Windows (за раз можно подключить до четырех уникальных контроллеров).
С помощью этого API можно запросить любой совместимый подключенный контроллер, и можно задать эффекты вибрации. Контроллеры, к которым подключена гарнитура, также могут запрашивать информацию о звуковых входных и выходных устройствах, которые можно использовать с гарнитурой для обработки звука.
Макет контроллера
Совместимые контроллеры имеют два аналоговых указателя, каждый из которых имеет цифровую кнопку, два аналоговых триггера, цифровую панель направления с четырьмя направлениями и восемь цифровых кнопок. Состояния каждого из этих входных данных возвращаются в структуре XINPUT_GAMEPAD при вызове функции XInputGetState.
Контроллер также имеет два двигателя вибрации, чтобы обеспечить эффекты обратной связи пользователю. Скорость этих двигателей указывается в структуре XINPUT_VIBRATION, передаваемой в функцию XInputSetState, чтобы задать эффекты вибрации.
При необходимости гарнитура может быть подключена к контроллеру. Гарнитура имеет микрофон для голосового ввода и наушники для вывода звука. Вы можете вызвать функцию XInputGetAudioDeviceIds или устаревшую функцию XInputGetDSoundAudioDeviceGuids, чтобы получить идентификаторы устройства, соответствующие устройствам для микрофона и наушников. Затем можно использовать 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, то в состоянии не было изменений. Если оно отличается, приложение должно проверить элемент Gamepad структуры 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_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_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] в формате плавающей точки (как показано в примере выше) и, при необходимости, применить нелинейное преобразование.
Например, в гоночных играх может быть полезно возводить результат в куб, чтобы обеспечить лучшее управление машинами с помощью геймпада. Возведение в куб позволяет добиться большей точности в нижних диапазонах, что желательно, поскольку игроки обычно или слегка нажимают, чтобы добиться плавного движения, или прикладывают полную силу в одном направлении, чтобы получить быстрый ответ.
Настройка эффектов вибрации
Помимо получения состояния контроллера, вы также можете отправлять данные вибрации контроллеру, чтобы изменить обратную связь, предоставленную пользователю контроллера. Контроллер содержит два хмыкающих двигателя, которые можно самостоятельно контролировать путем передачи значений в функцию 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 из набора SDK DirectX (XInput 1.3). В версии Windows 8 XInput (XInput 1.4) используются исключительно идентификаторы устройств Windows Audio Session API (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;