언리얼 엔진 5/공부

[언리얼 엔진 5] Retriggerable Delay 사용법 (FLatentActionInfo)

ciel45 2024. 4. 10. 20:38

게임 로직을 구성할 때, 특정 조건으로 인해 이벤트가 트리거되고, 일정 시간 동안 해당 조건이 발생하지 않아야 이벤트가 종료되어야 하는 상황이 있을 수 있다.

 

예를 들자면, FPS / TPS 게임에서 적에게 피격당했을 시 데미지 방향 표시기가 나타나는데, 

일정 시간 동안 추가적인 공격을 받지 않아야 표시기가 페이드 아웃되어 사라지고, 그 전에 추가 공격을 받을 시 표시기가 업데이트된다.

 

아래는 프로젝트 내에서 해당 로직을 사용한 상황이다.

https://ciel45.tistory.com/62

 

블루아카이브 TPS게임 개발일지 (45) - 피격 방향 표시기 구현 2

이제 이 표시기가 피격 시 화면에 떠야 한다. 맞을 때마다 일일히 화면에 띄워주고, 없애주기 귀찮아 약간의 꼼수를 썼다. 사실은 항상 화면에 떠있는 채로 투명도를 0으로 하고, 피격당했을 시

ciel45.tistory.com

 

이러한 것을 구현하려면, 단순히 타이머를 쓰자니 곤란할 수 있다.

타이머는 기본적으로 정해진 시간이 끝나면 무조건 바인딩된 이벤트를 수행하도록 만들어져 있기 때문이다.

 

타이머를 대체할 만한 좋은 요소가 있는데, 블루프린트에서는 Retriggerable Delay 노드라고 불린다.

https://docs.unrealengine.com/4.26/en-US/BlueprintAPI/Utilities/FlowControl/RetriggerableDelay/

 

Retriggerable Delay

Retriggerable Delay

docs.unrealengine.com

 

노드가 실행되면 duration 이후 completed에 달린 로직이 수행된다.

duration 중간에 노드가 한번 더 실행되면, 카운트다운을 초기화해서 다시 시작한다.

 

C++에서는 할 것이 조금 더 있다.

UKismetSystemLibrary::RetriggerableDelay() 함수와 함께, 

FLatentActionInfo라는 구조체를 사용해야 한다.

https://docs.unrealengine.com/4.26/en-US/API/Runtime/Engine/Engine/FLatentActionInfo/

 

FLatentActionInfo

Latent action info.

docs.unrealengine.com

 

 

FLatentActionInfo 구조체를 RetriggerableDelay 함수의 인자로써 넣어주는 방식으로 사용 가능하다.

아래는 프로젝트에서 사용한 예시이다.

// KannaCharacter.h에 구조체 생성
FLatentActionInfo HealthInfo;
FLatentActionInfo FadeInfo;
void AKannaCharacter::BeginPlay()
{
	Super::BeginPlay();

	/* 생략 */

	//FLatentInfo 초기화
	
	FadeInfo.CallbackTarget = this;
	FadeInfo.Linkage = 0;
	FadeInfo.ExecutionFunction = FName("FadeOutDamageIndicator");
	FadeInfo.UUID = 1;

	HealthInfo.CallbackTarget = this;
	HealthInfo.Linkage = 0;
	HealthInfo.ExecutionFunction = FName("EnableHealthRegen");
	HealthInfo.UUID = 2;
}

 

CallbackTarget으로는 바인딩할 함수가 있는 오브젝트를 할당하면 된다. 여기서는 이 오브젝트 자기자신이다.

 

Linkage는 execute할 함수 안에서의 resume point를 지정하는 것인데, 처음부터 하면 되므로 0으로 했다.

 

ExecutionFunction이 중요한데, execute할 함수를 FName으로 지정해준다.

 

UUID는 RetriggerableDelay의 작동에 있어 매우 중요하다. UUID를 통해 FLatentActionInfo를 구별하기 때문이다.

UUID가 같은 구조체가 RetriggerableDelay에 들어가 호출된다면, 기존의 그 UUID의 FLatentActionInfo 를 쓰던 RetriggerableDelay의 카운트다운을 초기화할 지 말 지 결정한다. 따라서 각 FLatentActionInfo 마다 유일한 숫자로 해놓아야 한다.

 

구조체를 만들었다면, RetriggerableDelay에 (GetWorld(), <원하는 카운트다운 duration>, <만든 구조체>)를 넣어 호출하면 된다.

float AKannaCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)													
{
	if (Attributes && CanBeDamaged())
	{
		/* ... */
        // 특정 조건에 따라 RetriggerableDelay 호출
		UKismetSystemLibrary::RetriggerableDelay(GetWorld(), 3.f, HealthInfo);
	}

	if (DamageIndicator && EventInstigator)
	{
		/* ... */
        // 특정 조건에 따라 RetriggerableDelay 호출
		UKismetSystemLibrary::RetriggerableDelay(GetWorld(), 3.f, FadeInfo);
	}

	/* ... */
}

 

특정 조건 시 FLatentActionInfo가 작동하도록 만든 코드이다.

 

 

타이머로도 조건이 다시 트리거될 시 기존 타이머를 무효화하고 다시 카운트하는 방식으로 어찌어찌 만들 수는 있겠지만, FLatentActionInfo와 RetriggerableDelay를 사용하면 편하게 만들 수 있다.