TIL_082 : InputTrigger vs ETriggerEvent

김펭귄·2025년 12월 16일

Today What I Learned (TIL)

목록 보기
82/93

오늘 학습 키워드

  • InputTrigger vs ETriggerEvent

1. 동일한 버튼에 짧은/긴 입력 바인딩

1.1. 문제 상황

  • 마우스 우클릭을 짧게 클릭했을때와 길게 누르고 있을 때의 동작 차이를 두고 싶었음

  • 그래서 먼저 Input Action에서 Trigger에 Hold를 추가. 0.3초를 임계치로 설정함.
    길게 입력이 들어왔을 때 함수를 한 번만 호출하면 되어서, IsOneShot도 참으로 설정

  • IMC에서는 별 다른 수정 없이 사용

  • 그리고 Input Action의 BindAction을 아래의 코드처럼 구현하였음.
    ETriggerEvent::CanceledTriggered되지 못 할때의 상황이므로 임계치를 넘기지 못 한 짧은 클릭에 적합하다 생각.
    ETriggerEvent::Triggered를 통해 임계치 시간을 넘긴 입력이 들어왔을 때 바로 실행되도록 하였고, ETriggerEvent::Completed를 통해 긴 입력이 끝났을 때 함수가 실행되도록 함

EnhancedInput->BindAction(RightClickAction, ETriggerEvent::Canceled,
						  this, &AMyCharacter::ShortRightClick);
EnhancedInput->BindAction(RightClickAction, ETriggerEvent::Triggered, 
						  this, &AMyCharacter::LongRightClickStart);
EnhancedInput->BindAction(RightClickAction, ETriggerEvent::Completed, 
						  this, &AMyCharacter::LongRightClickEnd);

1.2. 또 다른 문제 상황

  • 짧은 입력은 잘 동작하였고, 우클릭을 임계치 이상으로 길게 하였을 때도 시작함수가 잘 호출되었음.
    다만, LongRightClickStart함수가 호출되자마자, 바로 LongRightClickEnd함수도 같이 호출되었음

  • 이유로는 Is One Shot을 참으로 하게되면, Triggered가 되었을 때, 더 이상 입력이 트리거 되었는지를 확인할 필요가 없어 상태를 None으로 바꾸게 되고 이 때문에 Completed가 바로 호출되는 것

ETriggerState UInputTriggerTimedBase::UpdateState_Implementation(/**/)
{
	ETriggerState State = ETriggerState::None;

	// Transition to Ongoing on actuation. Update the held duration.
	if (IsActuated(ModifiedValue))
	{
    		// ... //
	}
	else
	{
		// Reset duration
		HeldDuration = 0.0f;
	}

	// trigger 체크 끝났으니 None이 나옴
	return State;
}
  • 간단한 해결법으로는 Is One Shot을 거짓으로 하면 원하는 대로 길게 눌렀을 때 Start함수가, 버튼에서 손을 떼면 End함수가 호출되지만, 매 프레임마다 Start함수가 계속 호출됨

  • 호출이 한 번만 필요한 함수를 계속 호출하는 것도 오버헤드라 생각하여 다른 방법을 찾아봄

1.3. 해결방법

  • 짧은 클릭과 긴 클릭에 해당하는 input action을 2개 만들어보고, 긴 클릭은 Triggers는 Hold로 설정하는 방식으로 해보았지만, 클릭만 해도 두 개 다 실행됨

  • 그래서 나만의 Trigger를 새로 만들어보기로 함. 어차피 IsOneShot이 참일때 Triggered 이후 바로 None이 되었던 것이 문제였음. 그래서 Triggered 이후에 바로 None이 안 되게 해보려고 함

  • final 키워드 : 이 클래스로 상속 못 하게 하는 키워드. 그래서 UInputTriggerHold는 상속 못 해서, UInputTriggerTimedBase을 상속함

// RightClick_InputTriggerTimedBase.h
UCLASS()
class TARCOPY_API URightClick_InputTriggerTimedBase : public UInputTriggerTimedBase
{
	GENERATED_BODY()

	bool bTriggered = false;

protected:

	virtual ETriggerState UpdateState_Implementation(/**/) override;
    
    // ... //
	
};

// RightClick_InputTriggerTimedBase.cpp
ETriggerState URightClick_InputTriggerTimedBase::UpdateState_Implementation(/**/)
{
	ETriggerState State = Super::UpdateState_Implementation(/**/);

	// 첫 번째 트리거인지 확인
	bool bIsFirstTrigger = !bTriggered;
    // 계속 키 누르면 시간 계속 증가하고 계속 트리거 된 상태됨(틱마다)
	bTriggered = HeldDuration >= HoldTimeThreshold;	

	if (bTriggered)
	{
		return (bIsFirstTrigger || !bIsOneShot) ?
        	ETriggerState::Triggered : 
            ETriggerState::None;
	}

	return State;
}
  • 근데 애초에 OneShot이든 아니든 Hold 자체가 매 틱마다 내부적으로 함수가 돌고 있었음.
    즉, 키를 길게 눌러 임계치 이상 될 때 한번만 작동하길 원했지만 애초에 입력이 들어가면 내부적으로 틱마다 처리 로직이 동작함

  • 그래서 그냥 Is One Shot을 거짓으로 하는 간편한 방법을 사용하였고, InputTrigger에 대해 더 공부해보았음

2. Trigger vs ETriggerEvent

  • Input Action과 Input Mapping Context에서 존재하는 Trigger와 BindAction 해줄 때의 ETriggerEvent에 대한 설명

  • 전자는 키 입력이 들어왔을 때, 각 Trigger 조건에 따라 Input Action의 TriggerState를 바꿔줌

  • 그럼 바뀐 Input Action의 TriggerState에 따라서 알맞은 Bind한 함수가 호출됨

2.1. Trigger

  • 위 영상처럼 각 트리거에 따라 input이 언제 IA의 TriggerState를 바꿀지를 정함

2.2. TriggerState

  • 순서는 Started-Ongoing-Triggered-Completed 순으로 동작함

  • Started : 키가 눌렸으면 바로 이 상태로 전환됨

  • Triggered : 키 입력이 조건을 만족하여 트리거 된 상태.
    만약 트리거 조건에서 Hold였고, 트리거 된 후 IsOneShot이 참이면 None이 되고, 거짓이면 계속 Triggered 유지

  • Ongoing : Started(키 입력 시작) 이후 Triggered되기 전까지 조건을 만족하였는지 매 틱마다 계속 체크되는 상태

  • Canceled : Ongoing 시점(트리거 되기 전)에 입력이 끊긴 상태

  • Completed : 트리거 된 이후 입력이 끊긴 상태

  • 키 입력 시 무조건 Started로 상태가 바로 변하고, 조건이 따로 없다면 Triggered 상태가 바로 됨

profile
반갑습니다

0개의 댓글