본문 바로가기
둥지/그래픽스

DirectX XInput 다뤄보기

by 까닭 2023. 2. 8.

DirectX Input 컴포넌트

DirectX SDK로 게임을 만들다보면 키보드가 아닌 게임 컨트롤러 입력을 받고 싶은 경우가 생길 수 있다. 이 때 크게 세 가지 선택지가 있는데, DirectInput과 XInput 그리고 Windows.Gaming.Input이다.

 

우선 DirectInput은 DirextX 1.0 때부터 사용된 역사 깊은 입출력 API이며 이후 XBOX 360 컨트롤러 위해 XInput이 DirextX 10.1 때 추가되었다.

 

https://docs.microsoft.com/ko-kr/windows/win32/xinput/xinput-and-directinput

 

XInput 및 DirectInput 기능 비교 - Win32 apps

XInput 및 DirectInput API 및 기능을 비교합니다.

learn.microsoft.com

 

하지만 XInput 또한 1.4 버전을 마지막으로 WGI(Windows.Gaming.Input)으로 대체되었는데, WGI를 통해 Windows 10 및 Windows11 그리고 Xbox의 UWP 게임에서 다양한 컨트롤러(레이싱 휠, 비행 조종 스틱)들을 지원할 수 있게 되었다.


https://learn.microsoft.com/ko-kr/windows/uwp/gaming/input-for-games

 

게임용 입력 - UWP applications

이 섹션에서는 UWP(유니버설 Windows 플랫폼) 게임용 게임 패드 및 기타 입력 장치의 사용 방법을 보여 줍니다.

learn.microsoft.com

 

이왕이면 DirextInput과 WGI도 같이 다루고 싶지만
현재까지 써본 라이브러리가 XInput 뿐이라서 나머지는 이후 기회가 생긴다면 한 번 다뤄보자.

 

🎮XInput 사용 방법

#include <Xinput.h>
#pragma comment(lib, "xinput.lib")

먼저 XInput 헤더를 포함하고 라이브러리를 추가해준다.

 

#define XINPUT_GAMEPAD_DPAD_UP          0x0001
#define XINPUT_GAMEPAD_DPAD_DOWN        0x0002
#define XINPUT_GAMEPAD_DPAD_LEFT        0x0004
#define XINPUT_GAMEPAD_DPAD_RIGHT       0x0008
#define XINPUT_GAMEPAD_START            0x0010
#define XINPUT_GAMEPAD_BACK             0x0020
#define XINPUT_GAMEPAD_LEFT_THUMB       0x0040
#define XINPUT_GAMEPAD_RIGHT_THUMB      0x0080
#define XINPUT_GAMEPAD_LEFT_SHOULDER    0x0100
#define XINPUT_GAMEPAD_RIGHT_SHOULDER   0x0200
#define XINPUT_GAMEPAD_A                0x1000
#define XINPUT_GAMEPAD_B                0x2000
#define XINPUT_GAMEPAD_X                0x4000
#define XINPUT_GAMEPAD_Y                0x8000

위 매크로들은 스틱과 LT, RT를 제외한 키(XBox 기준)들의 값들이다.

 

    DWORD dwResult;
    
    for (DWORD i = 0; i < XUSER_MAX_COUNT; i++)
    {
    	//컨트롤러 입력 상태 초기화
        XINPUT_STATE state;
        ZeroMemory(&state, sizeof(XINPUT_STATE));

    	//컨트롤러 입력 상태 받아오기
        dwResult = XInputGetState(i, &state);

        if (dwResult == ERROR_SUCCESS)
        {
            if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START)
            {
            
            }

            else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A)
            {

            }
        }
    }

위 코드는 컨트롤러의 입력 상태를 받아오는 코드이다.

 

XINPUT_STATE는 컨트롤러의 상태를 나타내는 구조체이며
멤버 변수로는 dwPacketNumber와 Gamepad를 가지고 있다.

 

dwPacketNumber는 상태 패킷 번호이며 패킷 번호는 컨트롤러 상태에 변경 사항이 있는지 여부를 나타낸다.

리시버와 컨트롤러가 연결되지 않았을 경우 0, 정상적으로 연결되었거나

유저가 컨트롤러를 조작하면 1이나 1 이상의 값이 반환된다.

 

    typedef struct _XINPUT_STATE
    {
        DWORD                               dwPacketNumber;
        XINPUT_GAMEPAD                      Gamepad;
    } XINPUT_STATE, * PXINPUT_STATE;

XInput.h에 들어가보면 XUSER_MAX_COUNT의 값이 4인 걸 볼 수 있는데
이는 한 번에 연결될 수 있는 컨트롤러의 수가 최대 4개이기 때문이다.

 

ZeroMemory( &state, sizeof(XINPUT_STATE));

ZeroMemory() 함수로 컨트롤러의 상태를 초기화한 후

 

dwResult = XInputGetState( i, &state );

XInputGetState()으로 컨트롤러의 상태를 다시 얻어온다.

 

    if (dwResult == ERROR_SUCCESS)
    {
        //키입력을 확인할 부분
    }

우리가 키입력을 확인할 부분은 위 if문 안에 추가해야 한다.

 

    if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START)
    {

    }

    else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A)
    {

    }

&연산자를 통해서 비트 연산을 수행한다.

 

    state.Gamepad.sThumbLX	// 왼쪽 스틱 X축
    state.Gamepad.sThumbLY	// 왼쪽 스틱 Y축
    state.Gamepad.sThumbRX	// 오른쪽 스틱 X축
    state.Gamepad.sThumbRY	// 오른쪽 스틱 Y축

    state.Gamepad.bLeftTrigger	// 왼쪽 트리거
    state.Gamepad.bRightTrigger	// 오른쪽 트리거

스틱과 트리거(LT, RT)는 정수를 통해서 키입력을 체크한다.

 

스틱들의 변수들 자료형은 short로 선언되어 있고 트리거의 변수들 자료형은 unsigned char로 선언되어 있다.

때문에 감지된 스틱의 최댓값은 short의 범위인 32767이며 트리거의 최대값은 unsigned char의 최대 범위인 255이다.

 

즉, 스틱이나 트리거를 반쯤 움직이면 값은 각각 32767 / 2, 255 / 2가 되는 셈.

 

XINPUT_VIBRATION vibration;
ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
vibration.wLeftMotorSpeed = 65535; //왼쪽 진동 모터의 세기
vibration.wRightMotorSpeed = 0;	//오른쪽 진동 모터의 세기
XInputSetState(0, &vibration);

마지막으로 컨트롤러 진동에 관한 코드를 살펴보자.

 

컨트롤러의 진동 모터가 오른쪽과 왼쪽, 양쪽에 탑재되어 있기 때문에
진동의 세기값 변수 또한 두 개이다.

 

진동의 세기 변수는 unsigned short로 선언되어 있어서
최대 65535까지 컨트롤러 세기를 조절할 수 있다.

 

그럼 이제 위 코드를 통해 게임 속 컨트롤러 입력을 구현해보자.

'둥지 > 그래픽스' 카테고리의 다른 글

깊이 버퍼(Depth Buffer)  (0) 2023.03.31