[언리얼엔진] 입력 개요
- ⭐ Game Programming/Unreal Document
- 2023. 5. 31.
플레이어 입력(PlayerInput) 오브젝트는 플레이어의 입력을 플레이어 컨트롤러 또는 폰과 같은 액터가 이해하고 사용할 수 있는 데이터로 변환하는 기능을 한다. 이는 플레이어의 하드웨어 입력을 플레이어 입력 매핑 및 입력 컴포넌트를 통한 움직임과 게임 이벤트로 변환하는 입력 처리 흐름의 일부다.
하드웨어 입력
플레이어의 하드웨어 입력은 일반적으로 키 누름, 마우스 클릭 또는 마우스 움직임, 컨트롤러 버튼 누름 또는 조이스틱 움직임 등이 있다. 표준 축 또는 버튼 인덱스를 따르지 않거나 특이한 입력 범위를 갖는 특수 입력 디바이스는 RawInput 플러그인을 사용하여 수동으로 설정할 수 있다.
플레이어 입력
PlayerInput은 플레이어 입력을 관리하는 플레이어 컨트롤러 클래스 내의 UObject다. 클라이언트 측에서만 스폰된다. 두 구조체가 PlayerInput 내에서 정의된다. FInputActionKeyMapping 은 액션 매핑을 정의하고, FInputAxisKeyMapping 은 축 매핑을 정의한다. 액션 매핑과 축 매핑에 사용되는 하드웨어 입력 정의는 InputCoreTypes에서 확립한다.
액션 매핑
개별 버튼 또는 키 입력을 이후 이벤트 주도 행동에 바인딩될 '친근한 이름'으로 매핑한다. 최종 이펙트는 키, 마우스 버튼, 키패드 버튼을 누르거나 놓는 입력으로 일부 게임 행동을 직접 트리거한다.
축 매핑
키보드, 컨트롤러, 마우스 입력을 이후 이동과 같은 지속적인 게임 행동에 바인딩될 '친근한 이름'으로 매핑한다. 축 매핑에 매핑된 입력은 현재 입력값을 0이라고 보고해도 지속적으로 조사된다. 액션 매핑 입력으로 트리거되는 이산적 게임 이벤트는 분리된 반면에와 달리 축 매핑은 게임 행동 사이의 부드러운 전환을 가능하게 한다. 트랜지션이 가능하다. 예를 들어 플레이어 이동 등이 있다.
컨트롤러 조이스틱과 같은 하드웨어 축은 1(눌림) 또는 0(눌리지 않음)과 같이 분리된 입력이 아니라 입력의 강도를 제공한다. 축은 살짝 움직일 수도 있고 크게 움직일 수도 있으며, 그에 따라 캐릭터의 움직임이 달라질 수 있습니다. 이러한 입력 방법은 이동 입력에 스케일 조절 가능한 강도를 제공한다는 점이다. WASD 또는 화살표 같은 일반 이동 키 또한 축 매핑으로 지속적으로 조사되는 게임 행동에 매핑할 수 있다.
입력 매핑 설정하기
입력 매핑은 환경설정 파일에 저장되며, 프로젝트 세팅의 입력 섹션에서 편집 가능하다.
- 레벨 에디터에서 편집 > 프로젝트 세팅을 선택
- 프로젝트 세팅 탭에서 입력을 클릭
축 입력의 프로퍼티 변경
액션 매핑 편집
축 매핑 편집
입력 컴포넌트
입력 컴포넌트(InputComponent)는 보통 폰과 컨트롤러에 있지만 필요한 경우 다른 액터나 레벨 스크립트에서도 설정할 수 있다. 입력 컴포넌트는 프로젝트의 축 매핑과 액션 매핑을 C++ 코드 또는 블루프린트 그래프로 게임 액션에 연결한다.
입력 컴포넌트의 입력 처리 우선순위 스택은 다음과 같은 우선순위를 따른다.
- 최소로 최근에 사용한거부터 최대로 최근에 사용한것 까지 "입력 허용"이 활성화된 모든 액터
- 컨트롤러
- 레벨 스크립트.
- 폰
입력을 받은 입력 컴포넌트는 더 이상 스택 아래로 내려갈 수 없다.
입력 프로세싱 절차
예시 - 앞으로 이동
1인칭 템플릿에서 가져옴
- 플레이어의 하드웨어 입력 : 플레이어가 w를 누른다.
- 플레이어 입력 매핑 : 축 매핑이 w를 스케일이 1인 'MoveForward'로 변환
- 입력 컴포넌트 우선순위 스택 : 입력 컴포넌트 우선순위 스택을 거칠 때 'MoveForward' 입력의 첫 바인딩은 AFirstPersonBaseCodeCharacter 클래스다. 현재 플레이어의 폰으로, 입력 컴포넌트가 마지막으로 체크된다.
void AFirstPersonBaseCodeCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
// 게임플레이 키 바인딩 설정
check(InputComponent);
...
InputComponent->BindAxis("MoveForward", this, &AFirstPersonBaseCodeCharacter::MoveForward);
...
}
- 게임로직 : AFirstPersonBaseCodeCharacter의 MoveForward 함수가 실행.
void AFirstPersonBaseCodeCharacter::MoveForward(float Value)
{
if ( (Controller != NULL) && (Value != 0.0f) )
{
// 앞쪽 찾기
FRotator Rotation = Controller->GetControlRotation();
// 걷거나 떨어질 때 피치 제한
if ( CharacterMovement->IsMovingOnGround() || CharacterMovement->IsFalling() )
{
Rotation.Pitch = 0.0f;
}
// 해당 방향으로 이동 추가
const FVector Direction = FRotationMatrix(Rotation).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
터치 인터페이스
기본적으로 터치 디바이스에서 실행되는 게임에는 콘솔 컨트롤러처럼 두 가지 가상 조이스틱이 있다. 프로젝트 세팅(Project Settings) 의 입력(Input) 섹션에 있는 기본 터치 인터페이스(Default Touch Interface) 프로퍼티로 이를 변경할 수 있다. 이는 터치 인터페이스 구성 에셋을 가리킨다. 디폴트는 DefaultVirtualJoysticks이며 공유 엔진 콘텐츠에 있다. 카메라를 돌릴 필요가 없는 게임을 위해 왼쪽 스틱만 있는 버전인 LeftVirtualJoystickOnly 도 있다.
오브젝트 선택 툴 뷰 옵션(View Options) 세팅에서 엔진 콘텐츠 표시(Show Engine Content) 체크박스를 체크해야 하는 점을 유의하자
가상 조이스틱이 필요하지 않은 경우 디폴트 터치 인터페이스 프로퍼티만 지운다. 또한 항상 터치 인터페이스 표시(Always Show Touch Interface)에 체크하거나 PC 게임을 -faketouches로 실행하여 플랫폼과 무관하게 게임에 터치 인터페이스를 강제할 수 있다.
향상된 입력 플러그인
실험단계 기능인 향상된 입력 플러그인은 복잡한 입력처리나 런타임 제어 리매핑 등의 고급 입력 기능이 필요한 프로젝트에서 개발자가 엔진의 디폴트 입력 시스템을 쉽게 업그레이드하고 하위 호환도 확보하도록 지원한다. 이 플러그인은 방사상 데드존, 조화된 액션, 컨텍스트 입력 및 우선순위, 원본 입력 데이터의 필터링 및 프로세싱 확장 기능 등을 에셋 기반 환경에서 제공한다.
시작하기
플러그인을 활성화 하기위해 에디터의 편집(Edit) 드롭다운 메뉴를 열고 플러그인(Plugin) 을 선택한다. 플러그인 목록의 입력(Input) 섹션에서 향상된 입력 플러그인을 활성화하고 에디터를 재시작한다.
에디터가 재시작되면 프로젝트가 기본 언리얼 엔진 입력 핸들러 대신 향상된 입력 플러그인 클래스를 사용하도록 설정할 수 있다. 편집(Edit) 드롭다운 메뉴에서 프로젝트 세팅(Project Settings) 을 선택한다. 엔진(Engine) 소제목 아래의 입력(Input) 섹션에서 Default Classes 세팅을 찾는다. 초기에는 표준 PlayerInput 및 InputComponent 클래스가 포함되어 있다.
향상된 입력을 사용하려면 Dafulat Player Input Class를 EnhancedPlayerInput 으로 Default Input Component Class를 EnhancedInputComponent 로 변경한다
핵심 개념
향상된 입력 시스템에는 다음 4가지 주요 개념이 있다.
- 입력 액션은 향상된 입력 시스템과 프로젝트 코드 사이의 커뮤니케이션 링크이다. 입력 액션은 점프, 문 열기 등 인터랙티브 캐릭터가 할 수 있는 모든 것을 아우르며, 버튼을 길게 눌러서 걷기를 달리기로 바꾸는 등 사용자 입력 상태를 나타내는 데도 사용될 수 있다. 입력 액션은 원본 입력과 구분된다. 입력 액션은 어떤 입력에 의해 트리거됐는지와 무관하게 현재 상태를 알고 있으며 입력 값을 최대 3개의 독립 부동 소수점 축에서 보고할 수 있다. 예를 들어 '아이템 획득' 액션은 사용자가 뭔가를 획득했는지 여부를 나타내는 켜기/끄기 상태만을 필요로 하는 반면 '걷기' 액션은 사용자가 걷는 방향과 속도를 나타내는 두 개의 축을 필요로 한다.
- 입력 매핑 컨텍스트(Input Mapping Context)는 사용자의 입력을 액션으로 매핑하며 각 사용자에 대해 동적으로 추가, 제거, 우선순위 지정될 수 있다. 향상된 입력의 로컬 플레이어 서브시스템을 통해 로컬 플레이어에게 하나 이상의 컨텍스트를 적용할 수 있으며, 동일한 입력을 소화하려는 여러 액션 사이의 충돌을 해소하도록 우선순위를 지정할 수 있다. 이에 대한 일반적인 예시는 캐릭터가 월드에서 걷는 동안 문을 여는 버튼, 또는 캐릭터의 가방을 보는 동안 아이템을 선택하는 버튼 등이다. 캐릭터가 가방을 열 때마다 '아이템 선택' 입력 매핑 컨텍스트를 추가하여 '문 열기' 컨텍스트보다 높은 우선순위를 부여하고, 캐릭터가 가방을 닫으면 '아이템 선택' 컨텍스트를 제거하는 것이다. 이를 통해 입력 처리 레벨에서 문과 가방 시스템을 인지하도록 코드를 프로그래밍하지 않아도 사용자의 입력을 캐릭터의 상황에 맞춰 올바르게 해석할 수 있습니다.
- 모디파이어(Modifiers)는 사용자 디바이스의 원본 입력 값을 조절한다. 입력 매핑 컨텍스트는 각 입력 액션의 원시 입력(raw input)과 관련된 모디파이어를 얼마든지 가질 수 있다. 일반적인 모디파이어로는 데드존, 다수 프레임에서 입력 스무딩, 입력 벡터를 로컬에서 월드 스페이스로 변환, 기타 플러그인에 포함되는 것들이 있다. 개발자는 자신만의 모디파이어를 생성할 수도 있다.
- 트리거는 모디파이어 이후 입력 값이나 다른 입력 액션의 출력 규모를 사용하여 입력 액션을 활성화할지 여부를 결정한다. 입력 매핑 컨텍스트 내의 입력 액션은 각 입력에 대해 하나 이상의 트리거를 가질 수 있다. 예를 들어 사진을 찍으려면 사용자는 왼쪽 마우스 버튼을 4분의 1초 동안 눌러야 하는데, 이때 카메라를 조준하는 별도의 입력 액션 또한 활성화되어 있는 것이다.
입력 액션
입력 액션은 시스템과 프로젝트 코드 사이의 연결 고리이다. 컨텍스트 브라우저(Context Browser) 에서 우클릭하고 입력(Input) 옵션을 펼친 다음 입력 액션(Input Action) 을 선택하여 입력 액션을 생성할 수 있다. 입력 액션을 트리거하려면 입력 매핑 컨텍스트에 포함시키고 해당 입력 매핑 컨텍스트를 로컬 플레이어의 향상된 입력 로컬 플레이어 서브시스템(Enhanced Input Local Player Subsystem) 에 추가해야 한다.
폰 클래스가 트리거된 입력 액션에 반응하게 하려면 SetupPlayerInputComponent에서 해당 폰 클래스를 적절한 유형의 트리거 이벤트(Trigger Event) 에 바인딩해야 한다. 다음은 SetupPlayerInputComponent를 오버라이드하여 입력 액션인 MyInputAction과 MyOtherInputAction을 핸들러 함수에 바인딩하는 샘플 코드다.
// UEnhancedInputComponent를 사용하는지 확인해야 한다. 그러지 않으면 프로젝트가 제대로 설정되지 않음.
if (UEnhancedInputComponent* PlayerEnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
{
// UInputAction*을 핸들러 함수 및 관심 대상인 각종 ETriggerEvent에 바인딩하는 방법은 여러 가지.
// 액션 버튼을 누를 때와 같이 MyInputAction이 시작될 때 틱에서 핸들러 함수를 호출.
if (MyInputAction)
{
PlayerEnhancedInputComponent->BindAction(MyInputAction, ETriggerEvent::Started, this, &AMyPawn::MyInputHandlerFunction);
}
// 이동 키를 길게 누를 때 등 입력 조건이 충족되면 모든 틱에서 이름으로 핸들러 함수(UFUNCTION)를 호출.
if (MyOtherInputAction)
{
PlayerEnhancedInputComponent->BindAction(MyOtherInputAction, ETriggerEvent::Triggered, this, TEXT("MyOtherInputHandlerFunction"));
}
}
입력 액션에는 블루프린트 스크립팅에 노출되는 내장 이벤트가 있다. C++ 개발자는 블루프린트 스크립팅 개발자에게 입력 액션 이벤트 액세스를 제공하려고 패스스루 함수를 만들 필요가 없다. 일반적인 핸들러 함수는 다음과 같은 양식을 취할 수 있다.
void AMyPawn::MyFirstAction(const FInputActionValue& Value)
{
// 핸들러 함수가 실행되고 있음을 확인해 주는 디버그 로그 출력.
UE_LOG(LogTemp, Warning, TEXT("%s called with Input Action Value %s (magnitude %f)"), TEXT(__FUNCTION__), *Value.ToString(), Value.GetMagnitude());
// GetType() 함수를 사용하여 값의 유형을 결정하고, [] 연산자를 0~2 사이의 인덱스와 함께 사용하여 데이터에 액세스.
}
대부분의 사용 사례에서 void (const FInputActionValue&) 시그니처를 사용하는 것이 권장된다. 입력 액션을 핸들러 함수와 바인딩하면 폰이 트리거된 특정 방식에 따라 반응하게 만들 수 있다. 가장 일반적인 트리거 유형은 버튼을 누른 즉시 한 번 발생하는 Started, 입력을 누르고 있는 동안 매 프레임마다 지속적으로 액션이 발생하는 Triggered 등이 있으며, 전체 목록은 Plugins/EnhancedInput/ETriggerEventETriggerEvent의 API 레퍼런스 페이지에서 볼 수 있다.
입력 매핑 컨텍스트
하나 이상의 입력 액션을 트리거하는 규칙을 입력 매핑 컨텍스트(Input Mapping Context)라고 한다. 최상위 레벨의 입력 액션 목록을 갖춘 계층구조를 기본구조로 한다. 입력 액션 레벨 아래에는 키, 버튼, 움직임 축 등 각 입력 액션을 트리거할 수 있는 사용자 입력 목록이 있다. 하단 레벨에는 각 사용자 입력에 대한 입력 트리거와 입력 모디파이어 목록이 있다. 여기서는 입력의 원본 값을 어떻게 필터링하고 프로세스할지, 어떤 제한을 충족해야 계층구조 상위의 입력 액션을 주도할 수 있는지 결정한다. 입력은 다수의 입력 모디파이어와 입력 트리거를 가질 수 있다. 각 단계의 출력을 다음 단계의 입력으로 사용하여 순차적으로 평가된다.
입력 매핑 컨텍스트를 생성하려면 컨텍스트 브라우저(Context Browser) 에서 우클릭하고 입력(Input) 옵션을 펼친 다음 입력 매핑 컨텍스트(Input Mapping Context) 를 선택한다.
입력 매핑 컨텍스트에 연관성 있는 입력 액션을 모두 채우자. 간단한 프로젝트의 경우 입력 매핑 컨텍스트 하나에 모든 입력 액션을 넣는 것이 가능할 수도 있다. 보다 복잡한 프로젝트는 다수의 입력 매핑 컨텍스트가 있어야 더 잘 작동한다. 로컬 플레이어가 한 번에 하나 이상의 활성 입력 매핑 컨텍스트를 가질 수 있기 때문. 또한 상호 배타적인 입력 매핑 컨텍스트를 통해 서로 다른 입력 액션에 사용되는 사용자 입력 간의 입력 콜리전을 피할 수 있다.
위 입력 매핑 컨텍스트는 달리기를 위한 입력 액션이다. 게임패드의 왼쪽 섬스틱 기울이기 등 다수의 입력을 통해 실행되며, 두 축을 하나의 입력으로 결합한다. 위 입력의 원본 값은 '데드존' 입력 모디파이어를 지나며, 결과 값은 '홀드' 입력 트리거로 전달되어 'RunAction' 입력 액션을 주도한다.
드롭다운 목록에서 다수의 입력 바인딩을 이용할 수 있다. 입력 바인딩을 더 빠르게 선택하려면 드롭다운 왼쪽의 작은 버튼을 누른 다음 바인딩하고 싶은 키나 버튼을 누르면 된다.
위 입력 매핑 컨텍스트는 달리기와 점프를 위한 입력 액션을 지원한다. 입력 매핑 컨텍스트를 채우면 폰의 플레이어 컨트롤러와 연결된 로컬 플레이어에게 이를 추가할 수 있다. 이를 위해 PawnClientRestart 함수를 오버라이드하고 다음과 같은 코드 블록을 추가하였다.
// 유효한 플레이어 컨트롤러가 있는지 확.
if (APlayerController* PC = Cast<APlayerController>(GetController()))
{
// 향상된 입력 로컬 플레이어 서브시스템을 플레이어 컨트롤러와 관련된 로컬 플레이어로부터 가져옴
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
{
// PawnClientRestart가 액터의 수명에서 2회 이상 실행될 수 있으므로 잔여 매핑을 우선 제거
Subsystem->ClearAllMappings();
// 각 매핑 컨텍스트를 우선순위 값과 함께 추가. 높은 값이 낮은 값에 우선
Subsystem->AddMappingContext(MyInputMappingContext, MyInt32Priority);
}
}
입력 매핑 컨텍스트를 추가하면 폰이 SetupPlayerInputComponent에 바인딩했거나 블루프린트 스크립팅 사용자가 구성한 입력 액션 이벤트에 반응할 수 있게 된다. 게임플레이 도중 이용 가능한 입력 매핑 컨텍스트를 변경하는 이벤트가 발생하면 ClearAllMappings, AddMappingContext, RemoveMappingContext를 사용하여 이용 가능한 명령 세트를 동적으로 업데이트할 수 있다. 자세한 정보는 Plugins/EnhancedInput/IEnhancedInputSubsystemInterfaceIEnhancedInputSubsystemInterface API 레퍼런스 페이지를 참조하자.
입력 모디파이어
입력 모디파이어(Input Modifier)는 언리얼 엔진이 수신한 원본 입력을 입력 트리거로 전달하기 전에 변경하는 프리 프로세서이다. 모디파이어들은 축 순서 변경, '데드존' 구현, 축 입력을 월드 스페이스로 변환 등 다양한 작업을 수행한다. 입력 매핑 컨텍스트 내의 입력 액션과 연결된 각 입력은 사용자가 정의한 일련의 입력 모디파이어 시리즈를 통과한 이후에 해당 입력의 입력 트리거로 진행한다. 입력 모디파이어는 목록에 나열된 순서대로 적용되며 각 입력 모디파이어의 출력 값은 다음 입력 모디파이어의 입력 값이 된다. 향상된 입력 플러그인에 포함된 입력 모디파이어 목록 전체를 보려면 Plugins/EnhancedInput/UInputModifierUInputModifier API 레퍼런스 페이지를 참조하자. 아직 존재하지 않는 입력 모디파이어가 프로젝트에 필요하다면 UInputModifier 클래스를 직접 만들 수 있다.
입력 트리거
입력 트리거(Input Trigger)는 사용자 입력이 입력 모디파이어 목록을 통과한 뒤 입력 매핑 컨텍스트 내에서 해당되는 입력 액션을 활성화할지 여부를 결정한다. 대다수 입력 트리거는 최소 활성화 값을 확인하고 짧은 탭, 길게 누르기, 일반적인 '누르기' 또는 '놓기' 이벤트와 같은 패턴을 검증하여 입력 자체를 분석한다. 'Chorded Action' 입력 트리거만 예외적으로 이 규칙에서 다른 입력 액션이 트리거되어야 한다. 기본으로 입력에 대한 사용자 활동은 모든 틱에서 트리거된다. 향상된 입력 플러그인에 포함된 입력 트리거 목록 전체를 보려면 Plugins/EnhancedInput/UInputTriggerUInputTrigger API 레퍼런스 페이지를 참조하자. 향상된 입력 플러그인에 존재하지 않는 입력 트리거가 프로젝트에 필요하다면 UInputTrigger 클래스를 직접 만들 수 있다.
문서
https://docs.unrealengine.com/5.2/ko/input-overview-in-unreal-engine/