마우스 우클릭을 짧게 클릭했을때와 길게 누르고 있을 때의 동작 차이를 두고 싶었음
그래서 먼저 Input Action에서 Trigger에 Hold를 추가. 0.3초를 임계치로 설정함.
길게 입력이 들어왔을 때 함수를 한 번만 호출하면 되어서, IsOneShot도 참으로 설정

IMC에서는 별 다른 수정 없이 사용
그리고 Input Action의 BindAction을 아래의 코드처럼 구현하였음.
ETriggerEvent::Canceled은 Triggered되지 못 할때의 상황이므로 임계치를 넘기지 못 한 짧은 클릭에 적합하다 생각.
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);
짧은 입력은 잘 동작하였고, 우클릭을 임계치 이상으로 길게 하였을 때도 시작함수가 잘 호출되었음.
다만, 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함수가 계속 호출됨
호출이 한 번만 필요한 함수를 계속 호출하는 것도 오버헤드라 생각하여 다른 방법을 찾아봄
짧은 클릭과 긴 클릭에 해당하는 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에 대해 더 공부해보았음
Input Action과 Input Mapping Context에서 존재하는 Trigger와 BindAction 해줄 때의 ETriggerEvent에 대한 설명
전자는 키 입력이 들어왔을 때, 각 Trigger 조건에 따라 Input Action의 TriggerState를 바꿔줌
그럼 바뀐 Input Action의 TriggerState에 따라서 알맞은 Bind한 함수가 호출됨
순서는 Started-Ongoing-Triggered-Completed 순으로 동작함
Started : 키가 눌렸으면 바로 이 상태로 전환됨
Triggered : 키 입력이 조건을 만족하여 트리거 된 상태.
만약 트리거 조건에서 Hold였고, 트리거 된 후 IsOneShot이 참이면 None이 되고, 거짓이면 계속 Triggered 유지
Ongoing : Started(키 입력 시작) 이후 Triggered되기 전까지 조건을 만족하였는지 매 틱마다 계속 체크되는 상태
Canceled : Ongoing 시점(트리거 되기 전)에 입력이 끊긴 상태
Completed : 트리거 된 이후 입력이 끊긴 상태
키 입력 시 무조건 Started로 상태가 바로 변하고, 조건이 따로 없다면 Triggered 상태가 바로 됨