레이싱 휠 및 포스 피드백

이 페이지에서는 UWP(유니버설 Windows 플랫폼)용 Windows.Gaming.Input.RacingWheel 및 관련 API를 사용하여 Xbox One에서 레이싱 휠을 프로그래밍하는 기본 사항을 설명합니다.

이 페이지를 읽으면 다음을 알게 됩니다.

  • 연결된 레이싱 휠 및 사용자 목록을 수집하는 방법
  • 레이싱 휠이 추가 또는 제거된 사실을 감지하는 방법
  • 하나 이상의 레이싱 휠에서 입력을 읽는 방법
  • 포스 피드백 명령 전송 방법
  • 레이싱 휠이 UI 탐색 디바이스로 동작하는 방법

레이싱 휠 개요

레이싱 휠은 실제 레이스카 조종석의 느낌과 유사한 입력 장치입니다. 레이싱 휠은 자동차 또는 트럭을 특징으로 하는 아케이드 스타일과 시뮬레이션 스타일의 레이싱 게임 모두를 위한 완벽한 입력 장치입니다. 레이싱 휠은 Windows 10 또는 Windows 11 및 Xbox One UWP 앱에서 Windows.Gaming.Input 네임스페이스로 지원됩니다.

레이싱 휠은 다양한 가격대로 제공되며, 일반적으로 가격대가 상승함에 따라 더 많고 더 나은 입력 및 포스 피드백 기능을 제공합니다. 모든 레이싱 휠에는 아날로그 조종 핸들, 아날로그 스로틀, 브레이크 컨트롤, 몇 가지 핸들 부착 버튼이 설치되어 있습니다. 일부 레이싱 휠에는 아날로그 클러치 및 핸드 브레이크 컨트롤, 패턴 변환기, 포스 피드백 기능이 추가로 설치됩니다. 모든 레이싱 휠이 동일한 기능 모음을 갖추고 있는 것은 아니며 특정 기능에 대한 지원도 다를 수 있습니다. 예를 들어 조종 핸들이 지원하는 회전 범위나 패턴 변환기가 지원하는 기어의 수가 다양할 수 있습니다.

디바이스 성능

다양한 레이싱 휠은 다양한 옵션 장치 기능 세트와 이러한 기능에 대한 다양한 수준의 지원을 제공합니다. 단일 종류의 입력 장치 간의 이러한 변형 수준은 Windows.Gaming.Input API에서 지원하는 장치 간에 고유합니다. 또한 발생하는 대부분의 디바이스는 적어도 일부 선택적 기능 또는 기타 변형을 지원하게 됩니다. 그렇기 때문에 연결된 레이싱 휠 각각의 기능을 개별적으로 확인하고 게임에 적합한 모든 변형 기능을 지원하는 것이 중요합니다.

자세한 내용은 레이싱 휠 기능 확인을 참조하세요.

포스 피드백

일부 레이싱 휠은 단순한 진동이 아닌 스티어링 휠과 같은 제어축에 실제 힘을 가할 수 있는 진정한 포스 피드백을 제공합니다. 이 기능을 사용하여 게임에 더 큰 몰입감이 생기며(모의 충돌 충격, '도로 주행 느낌') 능숙한 운전에 대한 도전 의식이 높아집니다.

자세한 내용은 포스 피드백 개요를 참조하세요.

UI 탐색

사용자 인터페이스 탐색을 위해 다양한 입력 장치를 지원해야 하는 부담을 줄이고 게임과 장치 간의 일관성을 유지하기 위해 대부분의 물리적 입력 장치는 UI 탐색 컨트롤러라는 별도의 논리 입력 장치 역할을 동시에 수행합니다. UI 탐색 컨트롤러는 입력 디바이스에서 UI 탐색 명령에 대한 일반적인 어휘를 제공합니다.

아날로그 컨트롤과 다양한 레이싱 휠 간의 변형 정도에 특별히 중점을 두고 있기 때문에 일반적으로 디지털 D-패드와 보기, 메뉴, A, B, X, Y 버튼이 장착되어 있어 게임 패드와 버튼 구성이 흡사합니다. 이들 버튼은 게임 플레이 명령을 지원하기 위한 것이 아니며 레이싱 휠 버튼로 바로 액세스할 수 없습니다.

레이싱 휠은 UI 탐색 컨트롤러로서 탐색 명령의 필수 집합을 왼쪽 섬스틱, D-패드, 보기, 메뉴, A, B 버튼에 매핑합니다.

탐색 명령 레이싱 휠 입력
위로 D 패드 위로
아래로 D 패드 아래로
Left D 패드 왼쪽으로
Right D 패드 오른쪽으로
보기 보기 버튼
메뉴 메뉴 버튼
Accept A button
취소 B 버튼

또한 일부 레이싱 휠은 일부 선택적 탐색 명령 집합을 지원하는 다른 입력에 매핑할 수 있지만 명령 매핑은 디바이스마다 다를 수 있습니다. 이러한 명령도 지원하는 것이 좋지만 이러한 명령이 게임의 인터페이스를 탐색하는 데 필수적이지 않은지 확인합니다.

탐색 명령 레이싱 휠 입력
Page Up 다양함
Page Down 다양함
페이지 왼쪽 다양함
페이지 오른쪽 다양함
위로 스크롤 다양함
아래로 스크롤 다양함
왼쪽으로 스크롤 다양함
오른쪽으로 스크롤 다양함
컨텍스트 1 X 버튼(일반적으로)
컨텍스트 2 Y 버튼(일반적으로)
컨텍스트 3 다양함
컨텍스트 4 다양함

레이싱 휠 감지 및 추적

레이싱 휠의 감지 및 추적은 Gamepad 클래스 대신 RacingWheel 클래스를 제외하고 게임 패드에서와 똑같은 방식으로 작동합니다. 자세한 내용은 게임 패드 및 진동을 참조하세요.

레이싱 휠 읽기

관심 있는 레이싱 휠을 식별하면 입력을 수집할 준비가 된 것입니다. 그러나 익숙한 다른 종류의 입력과 달리 레이싱 휠은 이벤트를 발생시켜 상태 변경을 전달하지 않습니다. 대신, 플라이트 스틱을 직접 폴링하여 현재 상태의 규칙적인 판독값을 가져올 수 있습니다.

레이싱 휠 폴링

폴링은 정확한 시점에 레이싱 휠의 스냅샷을 캡처합니다. 이러한 입력 수집 방법은 대부분의 게임에 적합합니다. 게임의 논리는 일반적으로 이벤트 기반 방식이 아닌 결정적 루프로 실행되기 때문입니다. 또한 시간을 두고 수집된 여러 단일 입력보다 한 번에 수집된 입력에서 게임 명령을 해석하는 것이 일반적으로 더 간단합니다.

레이싱 휠은 GetCurrentReading을 호출하여 폴링합니다. 이 함수는 레이싱 휠의 상태가 포함된 RacingWheelReading을 반환합니다.

다음 예제에서는 레이싱 휠의 현재 상태를 폴링합니다.

auto racingwheel = myRacingWheels[0];

RacingWheelReading reading = racingwheel->GetCurrentReading();

레이싱 휠 상태 외에도 각 판독값에는 상태가 검색된 시점을 정확하게 나타내는 타임스탬프가 포함됩니다. 타임스탬프는 이전 판독값의 타이밍 또는 게임 시뮬레이션의 타이밍과 관련되는 데 유용합니다.

레이싱 휠 기능 결정

수많은 레이싱 휠 컨트롤이 선택 사항이거나 필요한 컨트롤에서도 다양한 변형을 지원하므로 레이싱 휠의 각 판독값에 수집된 입력을 처리하려면 각 레이싱 휠의 기능을 개별적으로 결정해야 합니다.

선택적 컨트롤은 핸드브레이크, 클러치 및 패턴 시프터입니다. 레이싱 휠의 HasHandbrake, HasClutch, HasPatternShifter 속성을 각각 읽어 연결된 레이싱 휠이 이러한 컨트롤을 지원하는지 여부를 확인할 수 있습니다. 속성 값이 true인 경우 컨트롤이 지원되며, 그렇지 않은 경우 지원되지 않습니다.

if (racingwheel->HasHandbrake)
{
    // the handbrake is supported
}

if (racingwheel->HasClutch)
{
    // the clutch is supported
}

if (racingwheel->HasPatternShifter)
{
    // the pattern shifter is supported
}

또한, 달라질 수 있는 컨트롤은 스티어링 휠과 패턴 변환기입니다. 스티어링 휠은 실제 휠이 지원할 수 있는 물리적 회전 정도에 따라 달라질 수 있으며 패턴 변환기는 지원하는 고유한 전방 기어 수에 따라 달라질 수 있습니다. 레이싱 휠의 MaxWheelAngle 속성을 읽어 실제 휠이 지원하는 가장 큰 회전 각도를 확인할 수 있습니다. 해당 값은 시계 방향(양수)의 최대 지원되는 물리적 각도이며, 시계 반대 방향(음수 도)에서도 지원됩니다. 레이싱 휠의 MaxPatternShifterGear 속성을 읽어 패턴 변환기에서 지원하는 전진 기어 최고 단계를 확인할 수 있습니다. 그 값은 지원되는 전진 기어 최고 변속 단계를 나타내므로 값이 4이면 해당 패턴 변환기는 후진, 중립, 1단, 2단, 3단, 4단 기어를 지원하는 것입니다.

auto maxWheelDegrees = racingwheel->MaxWheelAngle;
auto maxShifterGears = racingwheel->MaxPatternShifterGear;

마지막으로, 일부 레이싱 휠은 스티어링 휠을 통해 포스 피드백을 지원합니다. 연결된 레이싱 휠이 레이싱 휠의 WheelMotor 속성을 읽어 포스 피드백을 지원하는지 여부를 확인할 수 있습니다. 포스 피드백은 WheelMotornull이 아닌 경우 지원되며, 그렇지 않은 경우 지원되지 않습니다.

if (racingwheel->WheelMotor != nullptr)
{
    // force feedback is supported
}

이를 지원하는 레이싱 휠의 포스 피드백 기능을 사용하는 방법에 대한 자세한 내용은 포스 피드백 개요를 참조하세요.

버튼 읽기

각 레이싱 휠 버튼, 즉 4방향 D-패드, 기어 뒤로 이동기어 앞으로 이동 버튼, 16개의 추가 버튼은 버튼이 눌렸는지(아래쪽), 놓였는지(위쪽)를 나타내는 디지털 판독값을 제공합니다. 효율성을 위해 버튼 판독값은 개별 부울 값으로 표시되지 않습니다. 대신 모두 RacingWheelButtons 열거형으로 표현되는 단일 비트 필드로 압축됩니다.

참고 항목

레이싱 휠에는 보기메뉴 버튼와 같이 UI 탐색에 사용되는 여러 추가 버튼이 설치되어 있습니다. 이러한 버튼은 RacingWheelButtons 열거형의 일부가 아니며 레이싱 휠에 UI 탐색 장치로 액세스해야만 읽을 수 있습니다. 자세한 내용은 UI 탐색 디바이스를 참조하세요.

버튼 값은 RacingWheelReading 구조체의 Buttons 속성에서 읽습니다. 이 속성은 비트 필드이기 때문에 비트 마스킹은 관심 있는 버튼의 값을 격리하는 데 사용됩니다. 해당 비트가 설정된 경우에는 버튼이 눌리고(아래), 그러지 않은 경우에는 놓입니다(위).

다음 예제에서는 다음 기어 버튼을 눌렀는지 여부를 확인합니다.

if (RacingWheelButtons::NextGear == (reading.Buttons & RacingWheelButtons::NextGear))
{
    // Next Gear is pressed
}

다음 예제에서는 다음 기어 버튼을 놓았는지 여부를 확인합니다.

if (RacingWheelButtons::None == (reading.Buttons & RacingWheelButtons::NextGear))
{
    // Next Gear is released (not pressed)
}

버튼이 눌렸다가 놓이거나, 반대로 놓였다가 눌렸을 때 여러 개의 버튼이 눌렸거나 놓였는지 또는 일련의 버튼이 특정 방식으로 정렬되었는지(일부는 눌리고 일부는 놓임) 확인해야 하는 경우가 있습니다. 이러한 조건을 검색하는 방법에 대한 자세한 내용은 버튼 전환 감지복잡한 버튼 정렬 검색을 참조하세요.

휠 읽기

스티어링 휠은 -1.0 ~ +1.0의 아날로그 판독값을 제공하는 필수 컨트롤입니다. 값 -1.0은 가장 왼쪽의 휠 위치에 해당하며, +1.0 값은 가장 오른쪽 위치에 해당합니다. 스티어링 휠의 값은 RacingWheelReading 구조체의 Wheel 속성에서 읽습니다.

float wheel = reading.Wheel;  // returns a value between -1.0 and +1.0.

휠 판독값은 실제 레이싱 휠에서 지원하는 회전 범위에 따라 실제 휠의 다른 물리적 회전 정도에 해당하지만 일반적으로 휠 판독값의 크기를 조정하지는 않습니다. 회전의 더 큰 정도를 지원하는 바퀴는 정밀도가 더 뛰어납니다.

스로틀 및 브레이크 읽기

스로틀과 브레이크는 각기 부동 소수점 값으로 표현된 0.0(완전히 놓음)~1.0(완전히 눌림) 사이의 아날로그 판독값을 제공하는 필수 컨트롤입니다. 스로틀 컨트롤의 값은 RacingWheelReading 구조체의 Throttle 속성에서 읽습니다. 브레이크 컨트롤의 값은 Brake 속성에서 읽습니다.

float throttle = reading.Throttle;  // returns a value between 0.0 and 1.0
float brake    = reading.Brake;     // returns a value between 0.0 and 1.0

핸드브레이크와 클러치 읽기

핸드브레이크와 클러치는 각각 부동 소수점 값으로 표시되는 0.0(완전히 해제됨)과 1.0(완전히 결합됨) 사이의 아날로그 판독값을 제공하는 옵션 컨트롤입니다. 핸드브레이크 컨트롤의 값은 RacingWheelReading 구조체의 Handbrake 속성에서 읽습니다. 클러치 컨트롤의 값은 Clutch 속성에서 읽습니다.

float handbrake = 0.0;
float clutch = 0.0;

if(racingwheel->HasHandbrake)
{
    handbrake = reading.Handbrake;  // returns a value between 0.0 and 1.0
}

if(racingwheel->HasClutch)
{
    clutch = reading.Clutch;        // returns a value between 0.0 and 1.0
}

패턴 변환기 읽기

패턴 변환기는 서명된 정수 값으로 표시되는 -1 ~ MaxPatternShifterGear의 디지털 판독값을 제공하는 선택적 컨트롤입니다. 값이 -1 또는 0이면 각각 후진중립 기어에 해당합니다. 값이 플러스 쪽으로 증가할수록 전진 기어 변속 단계가 최대 MaxPatternShifterGear까지 커집니다. 패턴 변환기 값은 RacingWheelReading 구조의 PatternShifterGear 속성에서 읽어들입니다.

if (racingwheel->HasPatternShifter)
{
    gear = reading.PatternShifterGear;
}

참고 항목

패턴 변환기가 지원되는 경우 그 위치는 필수 버튼인 이전 기어다음 기어 버튼와 함께 배치되는데, 이러한 버튼 역시 플레이어 자동차의 현재 기어 상태에 영향을 미칩니다. 두 종류 모두 배치되어 있을 경우 이들 입력을 간단하게 통합하려면 자동 변속기 선택 시 패턴 변환기(및 클러치)를 무시하고 수동 변속기 선택 시 이전다음 기어 버튼을 무시하면 됩니다. 이는 레이싱 휠에 패턴 변환기 컨트롤이 설치되어 있는 자동차의 경우에만 해당합니다. 게임에 적합하지 않은 경우 다른 통합 전략을 구현할 수 있습니다.

InputInterfacing 샘플 실행

GitHub의 InputInterfacingUWP 샘플 앱은 레이싱 휠 및 다양한 종류의 입력 디바이스를 함께 사용하는 방법과 이러한 입력 디바이스가 UI 탐색 컨트롤러로 작동하는 방식을 보여 줍니다.

포스 피드백 개요

수많은 레이싱 휠이 포스 피드백 기능을 갖추고 있어 더욱 높은 몰입감과 도전 의지가 샘솟는 주행 경험을 제공합니다. 포스 피드백을 지원하는 레이싱 휠에는 일반적으로 휠 회전 축인 단일 축을 따라 스티어링 휠에 힘을 적용하는 단일 모터가 장착되어 있습니다. 강제 피드백은 Windows.Gaming.Input.ForceFeedback 네임스페이스를 통해 Windows 10 또는 Windows 11 및 Xbox One UWP 앱에서 지원됩니다.

참고 항목

포스 피드백 API는 여러 힘 축을 지원할 수 있지만 현재 레이싱 휠은 휠 회전 이외의 피드백 축을 지원하지 않습니다.

포스 피드백 사용

이 섹션에서는 레이싱 휠에 대한 프로그래밍 포스 피드백 효과의 기본 사항을 설명합니다. 피드백은 효과(포스 피드백 디바이스에 먼저 로드된 후 소리 효과와 유사한 방식으로 시작, 일시 중지, 다시 시작, 중지될 수 있는 효과)를 사용하여 적용됩니다. 그러나 먼저 레이싱 휠의 피드백 기능을 결정해야 합니다.

포스 피드백 기능 결정

연결된 레이싱 휠이 레이싱 휠의 WheelMotor 속성을 읽어 포스 피드백을 지원하는지 여부를 확인할 수 있습니다. WheelMotornull인 경우 포스 피드백이 지원되지 않습니다. 그렇지 않은 경우 포스 피드백이 지원되므로 모터의 영향을 받는 축과 같이 모터의 특정 피드백 기능 확인 작업을 계속할 수 있습니다.

if (racingwheel->WheelMotor != nullptr)
{
    auto axes = racingwheel->WheelMotor->SupportedAxes;

    if(ForceFeedbackEffectAxes::X == (axes & ForceFeedbackEffectAxes::X))
    {
        // Force can be applied through the X axis
    }

    if(ForceFeedbackEffectAxes::Y == (axes & ForceFeedbackEffectAxes::Y))
    {
        // Force can be applied through the Y axis
    }

    if(ForceFeedbackEffectAxes::Z == (axes & ForceFeedbackEffectAxes::Z))
    {
        // Force can be applied through the Z axis
    }
}

포스 피드백 효과 로드

포스 피드백 효과는 게임 명령에서 자율적으로 '재생'되는 피드백 디바이스에 로드됩니다. 제공되는 기본 효과는 다양합니다. 사용자 지정 효과는 IForceFeedbackEffect 인터페이스를 구현하는 클래스를 통해 만들 수 있습니다.

효과 클래스 효과 설명
ConditionForceEffect 디바이스 내의 현재 센서에 응답하여 가변 힘을 적용하는 효과입니다.
ConstantForceEffect 벡터에 상수 힘을 적용하는 효과입니다.
PeriodicForceEffect 벡터를 따라 파형에 의해 정의된 가변 힘을 적용하는 효과입니다.
RampForceEffect 벡터를 따라 선형으로 증가/감소하는 힘을 적용하는 효과입니다.
using FFLoadEffectResult = ForceFeedback::ForceFeedbackLoadEffectResult;

auto effect = ref new Windows.Gaming::Input::ForceFeedback::ConstantForceEffect();
auto time = TimeSpan(10000);

effect->SetParameters(Windows::Foundation::Numerics::float3(1.0f, 0.0f, 0.0f), time);

// Here, we assume 'racingwheel' is valid and supports force feedback

IAsyncOperation<FFLoadEffectResult>^ request
    = racingwheel->WheelMotor->LoadEffectAsync(effect);

auto loadEffectTask = Concurrency::create_task(request);

loadEffectTask.then([this](FFLoadEffectResult result)
{
    if (FFLoadEffectResult::Succeeded == result)
    {
        // effect successfully loaded
    }
    else
    {
        // effect failed to load
    }
}).wait();

포스 피드백 효과 사용

로드되면 레이싱 휠의 WheelMotor 속성에서 함수를 호출하거나 피드백 효과 자체에 대한 함수를 개별적으로 호출하여 모든 효과를 시작, 일시 중지, 다시 시작, 중지할 수 있습니다. 일반적으로 게임 플레이가 시작되기 전에 사용하려는 모든 효과를 피드백 디바이스에 로드한 다음 해당 SetParameters 함수를 사용하여 게임 플레이가 진행됨에 따라 효과를 업데이트해야 합니다.

if (ForceFeedbackEffectState::Running == effect->State)
{
    effect->Stop();
}
else
{
    effect->Start();
}

마지막으로 필요할 때마다 특정 레이싱 휠에서 전체 포스 피드백 시스템을 비동기적으로 사용하거나 사용하지 않도록 설정하거나 재설정할 수 있습니다.

참고 항목