다음을 통해 공유


Windows 애플리케이션에서 XInput 시작

XInput을 통해 Windows 애플리케이션이 컨트롤러 상호 작용(컨트롤러 진동 효과 및 음성 입력 및 출력 포함)을 처리합니다.

이 토픽에서는 XInput의 기능 및 애플리케이션에서 설정하는 방법에 대한 간략한 개요를 제공합니다. 이 문서의 내용은 다음과 같습니다.

XInput 소개

애플리케이션은 XInput API를 사용하여 Windows PC에 연결될 경우 게임 컨트롤러와 통신할 수 있습니다(한 번에 최대 4개의 고유 컨트롤러 연결 가능).

이 API를 사용하면 연결된 호환 가능 컨트롤러의 상태를 쿼리할 수 있으며 진동 효과를 설정할 수 있습니다. 헤드셋이 연결된 컨트롤러는 음성 처리를 위해 헤드셋 호환 가능한 사운드 입력 및 출력 디바이스에 대해서도 쿼리할 수 있습니다.

컨트롤러 레이아웃

호환되는 컨트롤러에는 각각 디지털 버튼, 2개의 아날로그 트리거, 방향이 4개인 디지털 방향 패드, 디지털 버튼 8개인 두 개의 아날로그 방향 스틱이 있습니다. 이러한 입력 상태에는 XInputGetState 기능 호출 시 XINPUT_GAMEPAD 구조로 각각 반환됩니다.

컨트롤러에는 사용자에게 힘 피드백 효과를 제공하는 진동 모터 두 개도 있습니다. 이러한 모터의 속도는 진동 효과를 설정하기 위해 XInputSetState 함수에 전달되는 XINPUT_VIBRATION 구조체에 지정됩니다.

선택적으로 헤드셋을 컨트롤러에 연결할 수 있습니다. 헤드셋에는 음성 입력용 마이크와 사운드 출력용 헤드폰이 있습니다. XInputGetAudioDeviceIds 또는 레거시 XInputGetDSoundAudioDeviceGuids함수를 호출하여 마이크 및 헤드폰의 디바이스에 해당하는 디바이스 식별자를 가져올 수 있습니다. 그런 다음 핵심 오디오 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 구조의 게임 패드 멤버를 확인해 더 자세한 상태 정보를 가져와야 합니다.

성능상의 이유로 프레임마다 '빈' 사용자 슬롯에 대해 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, and 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 디바이스 식별자를 사용하는 방법에 대한 자세한 내용은 스트림 캡처를 참조하세요.

DirectSound GUID 가져오기(레거시 DirectX SDK만 해당)

컨트롤러에 연결할 수 있는 헤드셋에는 마이크를 사용하여 녹음할 수 있고 헤드폰을 사용하여 소리를 재생할 수 있는 두 가지 기능이 있습니다. XInput API에서 이러한 함수는 IDirectSound8IDirectSoundCapture8 인터페이스를 사용하여 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;

프로그래밍 참조