게임 프로그래밍/언리얼

[언리얼] Enhanced Input System

라일라엘 2024. 12. 2. 01:02

Enhanced Input이란

기존에 사용하던 입력 시스템은 초기화 단계에서 어떤 입력이 어떤 키가 어떤 함수와 연결할지 설정하는 방식이었다. 이런 방식은 게임 초기에 바인딩을 하기 때문에 런타임 중에 키보드에서 게임패드로 입력 시스템이 바뀌는 것처럼 매핑이 변경되어야 할 때 수정이 어렵다는 문제가 있다.

Enhanced Input은 기존에 있던 입력시스템의 그런 문제를 해결하기 위해 등장한 새로운 입력 시스템이다.

Enhanced Input은 크게 두 가지 오브젝트로 나눌 수 있다.

  • 입력 액션
  • 입력 매핑 컨텍스트

입력 액션

입력 액션은 특정 키와 연결될 오브젝트이다. 기존 입력 방식에서는 키와 함수가 매핑되었지만, Enhanced Input System에서는 키와 입력 액션이 매핑되고, 입력 액션에 함수가 바인딩된다.

입력액션에는 어떤 타입의 값을 함수에 반환할지 고를 수 있다.

이는 4가지 타입을 반환할 수 있다.

  • bool
  • Axis1D - float
  • Axis2D - FVector2D(x, y)
  • Axis3D - FVector(x, y, z)

키가 눌리는 것을 트리거라고 하는데, 이 것엔 상태와 타입이 있다.

그중에 상태는 다음과 같다

  • Triggered: 액션이 트리거 되었다. 즉, 모든 트리거 요구 사항 평가를 완료했다는 의미이다.
  • Started: 트리거 평가 이벤트가 시작되었다. 예를 들어 트리거 타입이 '두 번 탭(Double tap)'일 때 처음 키를 누르면 ‘Started' 스테이트가 한 번 호출된다.
  • Ongoing: 트리거를 여전히 처리 중이다. 예를 들어, 트리거 타입이 ‘길게 누르기(Press and hold)’ 일 때 지정된 기간이 끝나기 전에 사용자가 버튼을 누르고 있다면 액션이 진행 중이다. 이 이벤트는 트리거에 따라 입력 값을 수신하면 작업이 평가되는 동안 틱마다 발동된다.
  • Completed: 트리거 평가 프로세스가 완료되었다.
  • Canceled: 트리거링이 취소되었다. 예를 들어, 사용자가 '길게 누르기' 액션이 트리거 되기 전에 버튼을 놓는 경우를 말한다.

입력 매핑 컨텍스트

입력 매핑 컨텍스트는 어떤 키를 눌렀을 때 어떤 입력 액션을 호출할 것인지 결정하는 오브젝트이다.

또한 입력 액션에 Trigger와 Modifier를 설정할 수 있다.

Trigger는 트리거 타입을 의미한다. 트리거 타입은 여러 가지 복합적인 입력을 쉽게 처리하기 위해 있다. 가령 더블클릭을 처리하기 위해선 입력 액션과 연결된 함수에서 계속 두 번 눌렸는지를 계속 감지해야 한다. 하지만 트리거 타입을 ‘더블탭’으로 하게 된다면, 유저가 더블클릭을 해야 바인딩된 함수가 호출된다.

Modifier는 바인딩된 함수에 전달할 값을 수정하는 기능이다. 예를 들어 WASD키를 눌렀을 때 이에 맞는 값을 Axis2D로 보내고 싶다고 가정해 보자. 기본 값은 0과 1 사이의 값이 X축에 들어가게 된다. 이는 어느 키를 눌러도 마찬가지이다. 만약 W키를 누르면 Y축에 값이 들어가길 원한다면 Y축에 들어간다고 Modifier에 세팅하면 된다.

입력 매핑 컨텍스트는 우선순위가 있다. 만약 두 개의 입력 매핑 컨텍스트가 있고 A키를 눌렀을 때 호출될 함수가 각각 다르다면, 우선순위가 더 높은 입력매핑 컨텍스트의 키만 동작한다. 우선순위는 런타임 중에 수정할 수 있으며, 런타임에 입력 매핑 컨텍스트를 추가하고 제거할 수 있다.

AMPPlayerController* PlayerController = CastChecked<AMPPlayerController>(GetController());
UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer());
if (Subsystem)
{
	// DefaultInputContext라는 InputMappingContext 객체를 연결한다.
	// 0은 우선순위이며, 값이 클수록 우선순위가 높다.
	Subsystem->AddMappingContext(DefaultInputContext, 0);
	//Subsystem->RemoveMappingContext(DefaultInputContext); // 제거하는 함수.
}

응용

입력 매핑 컨텍스트에는 어떤 키가 눌렸을 때 어떤 입력 액션이 실행될지 연결되어 있다. 그리고 입력 매핑 컨텍스트를 등록할 때 우선순위를 설정할 수 있는데, 우선순위가 가장 높은 것이 실행된다.

예를 들어 WASD키가 등록되어있는 IMC_Default라는 입력 매핑 컨텍스트가 있고, 마찬가지로 WASD키가 등록되어있는 IMC_Swimming이라는 밉력 매핑 컨텍스트가 있다. 여기에서 IMC_Default는 우선순위가 0이고, IMC_Swimming의 우선순위가 1이라면, WASD키를 누를때 IMC_Swimming에 연결되어있는 입력 액션만 수행된다.

이를 잘 활용하면 다음과 같이 사용할 수도 있다.

예를들어 플레이어가 물에 빠지면 수영을 하게 되고, 이동속도가 절반으로 줄어든다고 가정해 보자. Walk()라는 함수가 호출되면 평소 이동속도로 움직이고, Swim()이라는 함수가 호출되면 절반의 이동속도로 수영한다. 이때 IMC_Default와 IMC_Swimming이라는 입력 매핑 컨텍스트를 생성한다(설정은 위의 예시와 동일). IMC_Default는 입력을 받으면 Walk() 함수를 호출하고 , IMC_Swimming은 입력을 받으면 Swim() 함수를 호출한다. 이렇게 하면 물에 빠졌을 때 IMC_Swimming 객체를 우선순위 1로 등록해 뒀다가, 물 밖으로 나오면 IMC_Swimming 객체를 제거한다.

이렇게 하면 물에 빠졌을 땐 Swim() 함수가 호출되어 절반의 이동속도로 움직이게 될 것이고, 물에서 나오면 Walk()라는 함수가 호출되어 평소의 이동속도로 움직일 수 있게 될 것이다.