XInput을 사용하면 Windows 애플리케이션이 컨트롤러 상호 작용(컨트롤러 럼블 효과 및 음성 입력 및 출력 포함)을 처리할 수 있습니다.
이 항목에서는 XInput의 기능 및 애플리케이션에서 설정하는 방법에 대한 간략한 개요를 제공합니다. 여기에는 다음이 포함됩니다.
XInput 소개
애플리케이션은 XInput API를 사용하여 Windows PC에 연결될 때 게임 컨트롤러와 통신할 수 있습니다(한 번에 최대 4개의 고유 컨트롤러를 연결할 수 있습니다).
이 API를 사용하면 호환되는 연결된 컨트롤러의 상태를 쿼리할 수 있으며 진동 효과를 설정할 수 있습니다. 헤드셋이 연결된 컨트롤러는 음성 처리를 위해 헤드셋과 함께 사용할 수 있는 사운드 입력 및 출력 디바이스에 대해서도 쿼리할 수 있습니다.
컨트롤러 레이아웃
호환되는 컨트롤러에는 각각 디지털 버튼, 2개의 아날로그 트리거, 4개의 방향이 있는 디지털 방향 패드, 8개의 디지털 버튼이 있는 두 개의 아날로그 방향 스틱이 있습니다. 이러한 각 입력의 상태는 XInputGetState 함수가 호출되면 XINPUT_GAMEPAD 구조에서 반환됩니다.
컨트롤러에는 사용자에게 힘 피드백 효과를 제공하는 두 개의 진동 모터도 있습니다. 이러한 모터의 속도는 진동 효과를 설정하기 위해 XInputSetState 함수에 전달되는 XINPUT_VIBRATION 구조체에 지정됩니다.
필요에 따라 헤드셋을 컨트롤러에 연결할 수 있습니다. 헤드셋에는 음성 입력을 위한 마이크와 사운드 출력을 위한 헤드폰이 있습니다. XInputGetAudioDeviceIds 또는 레거시 XInputGetDSoundAudioDeviceGuids 함수를 호출하여 마이크 및 헤드폰의 디바이스에 해당하는 디바이스 식별자를 가져올 수 있습니다. 그런 다음 Core Audio API 사용하여 음성 입력을 수신하고 사운드 출력을 보낼 수 있습니다.
XInput 사용
XInput을 사용하는 것은 필요에 따라 XInput 함수를 호출하는 것만큼 간단합니다. XInput 함수를 사용하여 컨트롤러 상태를 검색하고 헤드셋 오디오 ID를 가져와 컨트롤러 럼블 효과를 설정할 수 있습니다.
여러 컨트롤러
XInput API는 언제든지 연결된 최대 4개의 컨트롤러를 지원합니다. XInput 함수는 모두 설정되거나 쿼리되는 컨트롤러를 식별하기 위해 전달되는 dwUserIndex 매개 변수가 필요합니다. 이 ID는 0-3 범위이며 XInput에 의해 자동으로 설정됩니다. 이 숫자는 컨트롤러가 연결된 포트에 해당하며 수정할 수 없습니다.
각 컨트롤러는 컨트롤러의 가운데에 있는 "빛의 고리"에 사분면을 점등하여 사용하는 ID를 표시합니다. 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 구조에서 검색되면 변경 내용을 확인하고 컨트롤러 상태에 대한 특정 정보를 가져올 수 있습니다.
XINPUT_STATE 구조체의 dwPacketNumber 멤버를 사용하여 XInputGetState마지막 호출 이후 컨트롤러의 상태가 변경되었는지 확인할 수 있습니다. dwPacketNumberXInputGetState대한 두 순차 호출 간에 변경되지 않으면 상태가 변경되지 않습니다. 다른 경우 애플리케이션은 XINPUT_STATE 구조의 Gamepad 멤버를 확인하여 자세한 상태 정보를 가져와야 합니다.
성능상의 이유로 모든 프레임마다 '빈' 사용자 슬롯에 대해 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.h에서 XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 및 XINPUT_GAMEPAD_TRIGGER_THRESHOLD 정의된 제공된 데드존을 사용할 수 있습니다.
#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 함수에 값을 전달하여 독립적으로 제어할 수 있는 두 개의 럼블 모터가 포함되어 있습니다.
각 모터의 속도는 다음과 같이 XInputSetState 함수에 전달되는 XINPUT_VIBRATION 구조체의 WORD 값을 사용하여 지정할 수 있습니다.
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 디바이스 식별자를 사용하는 방법에 대한 자세한 내용은 Stream캡처를 참조하세요.
DirectSound GUID 가져오기(레거시 DirectX SDK만 해당)
컨트롤러에 연결할 수 있는 헤드셋에는 마이크를 사용하여 소리를 녹음할 수 있고 헤드폰을 사용하여 소리를 재생할 수 있는 두 가지 기능이 있습니다. XInput API에서 이러한 함수는 IDirectSound8 및 IDirectSoundCapture8 인터페이스를 사용하여 DirectSound통해 수행됩니다.
헤드셋 마이크와 헤드폰을 적절한 DirectSound 인터페이스와 연결하려면 XInputGetDSoundAudioDeviceGuids호출하여 캡처 및 렌더링 디바이스에 대한 DirectSoundGUID를 가져와야 합니다.
메모
레거시 DirectSound 사용하는 것은 권장되지 않으며 Windows 스토어 앱에서는 사용할 수 없습니다. 이 섹션의 정보는 XInput의 DirectX SDK 버전(XInput 1.3)에만 적용됩니다. Windows 8 버전의 XInput(XInput 1.4)은 XInputGetAudioDeviceIds통해 얻은 WASAPI(Windows Audio Session API) 디바이스 식별자를 단독으로 사용합니다.
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;