언리얼에서 시간조건 함수호출 방법엔 몇가지가 존재한다.
가장 대표적인 방법이 Tick함수를 사용하는 것. 그러나 시스템에 부담을 줄 수 있어 대규모 프로젝트에선 사용하기가 조심스럽고 꺼려진다. Tick함수는 일단 켜놓으면 좋으나 싫으나 프레임당 신호를 보내기때문에, 덮어놓고 쓰다보면 부하가 생길 수 밖에 없다.
그의 훌륭한 대안 중 하나가 바로 FTimerHadle이다.
FTimerHandle은 언리얼엔진의 타이머 시스템에서 특정 타이머를 관리하고 추척하는데 사용되는 핸들 객체이다.
언리얼의 FTimerManager에서 생성된 타이머를 관리할 때 사용되며, 특정 타이머의 상태를 확인하거나 조작할 때 유용하다.
FTimerHadle은 다음과 같이 선언한다.
FTimerHadle MyTimerHandle;
타이머를 설정하는 방법은 다음과 같다.
Getworld()->GetTimerManager().SetTimer(MyTimerHandle, this, &MyActor::MyFunction, 2.0f, false);
각 매개변수는 다음과 같다
1. MyTimerHandle : 사용할 타이머핸들
2. this : 실행할 객체 (this는 현재 객체)
3. &MyActor::MyFunction : 실행할 함수
4. 2.0f : 타이머 주기(현재는 2초마다 실행)
5. false : 반복 여부(현재는 한번만 실행)
현재 객체가 AActor 또는 UWorld기반일 경우엔 다음과 같이 GetWorld()를 생략하고 사용할 수 있다.
GetWorldTimerManager().SetTimer(MyTimerHandle, this, &AMyActor::MyFunction, 2.0f, false);
AActor와 UWorld기반 객체는 자체적으로 GetWorld()를 호출한다.
매개변수 작성에는 람다함수를 사용할수도 있다.
GetWorldTimerManager().SetTimer(MyTimerHandle, [this](){MyFunction()}, 2.0f, false);
최근부터는 변수 캡쳐에는 항상 'this'를 명시해줘야한다.
람다함수를 이용해 작성하면 인자를 함께 넣어줄수도 있다.
GetWorldTimerManager().SetTimer(MyTimerHandle, [this, MyVariable](){MyFunction(MyVariable)}, 2.0f, false);
이 경우에도 'this'는 명시해줘야한다.
타이머가 현재 작동중 조건을 건다면 다음과 같이 쓸 수 있다.
if(GetWorldTimerManager().IsTimerActive(MyTimerHandle)) {}
작동 여부에 따라 코드를 진행시킬 수 있다.
타이머를 취하려면 다음과 같이 쓸 수 있다.
GetWorldTimerManager().ClearTimer(MyTimerHandle);
타이머 종료까지 남은시간은 다음과 같이 구할 수 있다.
GetWorldTimerManager().GetTimerRemaining(MyTimerHandle);
타이머 실행 후 경과된 시간은 다음과 같이 구할 수 있다.
GetWorldTimerManager().GetTimerElapsed(MyTimerHandle);
위와 같은 함수들을 이용하면 타이머를 이용해 다양한 기능을 구현할 수 있다.
얼마전, FTimerHandle을 이용해 도트 데미지를 입히고 또 FTimerHandle을 이용해서 중단하는 로직을 생각하고 구현했다.
void AWEPoisonItem::ActivateItem(AActor* Activator)
{
if (AWorkEightCharacter* PlayerCharacter = Cast<AWorkEightCharacter>(Activator))
{
if (!PlayerCharacter->GetPoisonState())
{
Super::ActivateItem(Activator);
if (Activator && Activator->ActorHasTag("Player"))
{
PlayerCharacter->PoisonAddiction(true);
GetWorldTimerManager().ClearTimer(StopPoisonDamageTimerHandle);
GetWorldTimerManager().SetTimer(
StopPoisonDamageTimerHandle,
this,
&AWEPoisonItem::EndDamage,
TotalDamageTime,
false
);
GetWorldTimerManager().ClearTimer(PoisonDamageTimerHandle);
GetWorldTimerManager().SetTimer(
PoisonDamageTimerHandle,
[this, Activator]()
{GiveDamage(Activator); },
DamageInterval,
true
);
}
StaticMesh->SetVisibility(false);
}
}
}
void AWEPoisonItem::GiveDamage(AActor* Activator)
{
if (AWorkEightCharacter* PlayerCharacter = Cast<AWorkEightCharacter>(Activator))
{
UGameplayStatics::ApplyDamage(
PlayerCharacter,
PoisonDamage,
nullptr,
this,
UDamageType::StaticClass()
);
if (PoisonDamageSound)
{
UGameplayStatics::PlaySoundAtLocation(
GetWorld(),
PoisonDamageSound,
PlayerCharacter->GetActorLocation()
);
}
}
}
void AWEPoisonItem::EndDamage()
{
UE_LOG(LogTemp, Warning, TEXT("ENdDamage"))
GetWorldTimerManager().ClearTimer(PoisonDamageTimerHandle);
GetWorldTimerManager().ClearTimer(StopPoisonDamageTimerHandle);
//DestroyItem();
if (ACharacter* PlayerCharacter = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0))
{
if (AWorkEightCharacter* WEPlayerCharecter = Cast<AWorkEightCharacter>(PlayerCharacter))
{
WEPlayerCharecter->PoisonAddiction(false);
}
}
}
근데 왜인지는 모르겠지만, 첫번째 아이템이 발동됐을땐 제대로 데미지도 들어가고 제 시간에 멈췄는데 두번째 아이템부턴 데미지가 제 시간에 멈추지 않고 영원히 반복되었다.
로그를 이용해 확인해봤을때 타이머는 잘 작동하는데 함수를 호출하지 못하는것 같았다.
아직 이유는 모르겠지만 타이머의 공유문제인것같기도 하다.
더 연구를 해서 이유를 밝혀내면 다시 업데이트해야겠다.
'게임 개발 공부 > 언리얼엔진' 카테고리의 다른 글
| UE5 | C++ - FPlatformTime::Second() (2) (0) | 2025.02.19 |
|---|---|
| UE5 | C++ - FPlatformTime::Seconds(); (0) | 2025.02.17 |
| UE5 | C++ 개발학습 - 인터페이스 (0) | 2025.02.12 |
| UE5 학습 - DataTable (1) | 2025.02.07 |
| UE5 학습 - 애니메이션 리타겟팅 (1) | 2025.02.06 |