C++ 로 코딩하면 어려운점이 많다. 나는 이 GetWorldTimerManager() 를 사용할 때마다 아주 머리가 아팠다. 블루프린트 에서는 이 노드가 3초 뒤에 실행 되었으면 좋겠는데? 싶으면 그냥 Delay 노드를 사용해 3초를 주면 되었는데 C++ 상에서 코드가 3초 뒤에 실행 되게 하려면 Tick() 에 코딩을 짜 Delta Time을 더해주거나 이 GetWorldTimerManager() 를 사용하는 방법이 있다. 전자는 너무 비효율 적이라 패스 하고 오늘은 GetWorldTimerManager() 에 대해 알아보겠다.
GetWorldTimerManager() 란?
GetWorldTimerManager 는 현재 월드의 타이머 관리자(TimerManager) 를 반환하는 함수이다. 타이머 관리자는 게임이 시작한 직후에 생성되며 게임 내에서 특정 함수나 이벤트를 일정 시간 간격으로 반복하거나 지연하는 데 사용된다. 더 정확하게는 게임이 시작되면 Unreal Engine 자체에서 월드를 생성한다. 월드는 게임의 레벨, 액터, 환경등 모든 게임 요소를 포함하는 컨테이너 역할을 하게 된다. 이중 월드 내에서 타이머를 관리하는 FTimerManager 인스턴스가 함께 생성되게 되는데, 이 인스턴스가 GetWorldTimerManager 를 통해 접근할 수 있는 타이머 관리자이다.
주요 함수
GetWorldTimerManager 는 여러 주요 기능을 하는 함수가 존재한다. 각각의 함수들의 인수들과 사용처를 알아보자.
SetTimer : 특정 함수를 일정 시간 후 실행하거나 반복 실행하도록 설정할 수 있게해준다.
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 3초 후 폭발 함수 호출
GetWorldTimerManager().SetTimer(ExplosionTimerHandle, this, &AMyActor::Explode, 3.0f, false);
}
void AMyActor::Explode()
{
// 폭발 로직 구현
UE_LOG(LogTemp, Warning, TEXT("Boom!"));
// 액터 파괴
Destroy();
}
이때 SetTimer 의 인수들을 보면 ExplosionTimerHandle, this, &AMyActor::Explode, 3.0f, false 가 있는데 이를 하나씩 설명해 보겠다. SetTimer 는 다음과 같은 인자들을 필요로한다.
1. FTimerHandle : FTimerHandle 타입의 변수로, 설정된 타이머를 식별하고 관리하는 데 사용된다. 타이머를 해제하거나 특정 타이머의 정보를 얻을 때 이 핸들을 사용한다. 위에서 ExplosionTimerHandle 이 사용되었는데 이는
FTimerHandle ExplosionTimerHandle; 로 선언되어 헤더파일에 존재하거나 생성자 또는 GetWorldTimerManager 위에 선언해 주어도 된다.
2. UObject Object* : UObject 타입의 포인터로 타이머에 의해 호출될 함수가 속한 객체의 포인터이다. 일반적으로 this 즉, 현재 액터 또는 컴포넌트의 인스턴스를 전달한다.
3. void (AActor::Function)() : 호출할 함수로 타이머가 만려 되었을때 호출될 함수의 포인터이다. 그렇기에 앰퍼센트 (&) 를 이용해 호출해준다. 함수 포인터는 클래스 이름 , :: , 함수 이름 순으로 구성된다. (&AMyActor::MyFunction) 함수 포인터의 반환 값과 매개변수 타입은 SetTimer 함수에 지정된 타입과 일치해야 한다.
4. Time : 시간 간격으로 타이머가 호출될 때 까지 걸리는 시간 (초 단위)이다. float 형 이기에 float 형 변수를 사용해 채워넣어도 된다.
5. bLoop : Bool 타입의 인수로 타이머를 반복 실행할지 여부를 설정한다. true 로 설정하면 타이머가 지정된 시간 간격마다 반복 실행된다. false 로 설정하면 타이머가 한 번만 실행된다.
추가로 초기 시작 지연 (InitialStartDelay) 타이머가 처음으로 실행되기까지 걸리는 시간 (초 단위)이다. 생략 가능하며, 기본값은 0이다.
ClearTimer : 설정된 타이머를 해제하는 데 사용된다.
FTimerHandle HealTimerHandle;
bool bIsHealing = false;
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 1초마다 Heal 함수 호출
GetWorldTimerManager().SetTimer(HealTimerHandle, this, &AMyActor::Heal, 1.0f, true);
}
void AMyActor::Heal()
{
if (CurrentHealth < MaxHealth && !bIsHealing)
{
bIsHealing = true;
// 체력 회복 로직 ...
if (CurrentHealth >= MaxHealth)
{
// 최대 체력 도달 시 타이머 해제
GetWorldTimerManager().ClearTimer(HealTimerHandle);
bIsHealing = false;
}
}
}
위의 코드를 보면 CurrentHelath 가 MaxHealth 에 근접하자마자 ClearTimer를 이용해 HealTimerHandle 을 해제해 주었다. 이렇게 되면 타이머가 반복 실행 중인 경우, 타이머가 즉시 중지된다. 또한 액터가 파괴되면 해당 액터에 설정된 모든 타이머는 자동으로 해제되기에 이를 생략해도 된다. ClearTimer 함수를 호출한 후에는 해당 FTimerHandle 을 재사용하여 다른 타이머를 설정할 수 있다. ClearTimer 는 FTimerHandle 을 인자로 받는다.
PauseTimer 및 UnpauseTimer : 설정된 타이머의 작동을 일시 정지하거나 재개하는데 사용된다.
FTimerHandle GameTimeTimerHandle;
bool bIsGamePaused = false;
void AMyGameMode::SetGamePause(bool bPause)
{
bIsGamePaused = bPause;
if (bIsGamePaused)
{
// 게임 일시 정지
GetWorldTimerManager().PauseTimer(GameTimeTimerHandle);
// 추가적인 일시 정지 처리 (UI, 액터 등)
}
else
{
// 게임 재개
GetWorldTimerManager().UnpauseTimer(GameTimeTimerHandle);
// 추가적인 재개 처리 (UI, 액터 등)
}
}
위의 ClearTimer 과 다르게 PauseTimer 은 타이머핸들을 해제하지 않고 작동을 일시 정지시킬 수 있다. 특히 이것은 위와같이 UI 에 많이 사용되는데 메뉴 화면을 볼 때 게임을 일시 정지 시켜준다 던가 UnpauseTimer 을 이용해 게임을 재개하는 방식으로 사용할 수 있다. PauseTimer 과 UnpauseTimer 모두 FTimerHandle 을 인자로 받는다.
GetTimerElapsed : TimerHandle 의 경과 시간을 얻는데 사용한다.
FTimerHandle MyTimerHandle;
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 10초 후 MyFunction 호출
GetWorldTimerManager().SetTimer(MyTimerHandle, this, &AMyActor::MyFunction, 10.0f, false);
}
void AMyActor::MyFunction()
{
// ...
}
void AMyActor::CheckElapsedTime()
{
// 타이머가 실행 중인지 확인
if (GetWorldTimerManager().IsTimerActive(MyTimerHandle))
{
// 타이머 경과 시간 (초 단위)
float ElapsedTime = GetWorldTimerManager().GetTimerElapsed(MyTimerHandle);
UE_LOG(LogTemp, Warning, TEXT("Elapsed Time: %f"), ElapsedTime);
}
}
위와 같이 경과 시간을 알아야 하거나 디버깅할 때 float 형으로 저장할 수 있게 도와주는 함수이다. CheckElaspedTime 함수를 살펴보면 float 형 변수인 ElaspedTime 을 선언해 GetTimerElapsed 로 초기화 해주는 모습을 볼 수 있다. GetTimerElapsed 는 FTimerHandle 을 인자로 받는다.
GetTimerRemaining : TimerHandle 의 남은 시간을 얻는데 사용한다.
FTimerHandle MyTimerHandle;
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// 5초 후 MyFunction 호출
GetWorldTimerManager().SetTimer(MyTimerHandle, this, &AMyActor::MyFunction, 5.0f, false);
}
void AMyActor::MyFunction()
{
// ...
}
void AMyActor::CheckRemainingTime()
{
// 타이머가 실행 중인지 확인
if (GetWorldTimerManager().IsTimerActive(MyTimerHandle))
{
// 타이머 남은 시간 (초 단위)
float RemainingTime = GetWorldTimerManager().GetTimerRemaining(MyTimerHandle);
UE_LOG(LogTemp, Warning, TEXT("Remaining Time: %f"), RemainingTime);
}
}
위의 GetTimerElapsed 와는 반대로 남은 시간을 알아야 할때 사용한다. 이 또한 float 형 으로 반환되며 FTimerHandle 을 인자로 받는다.
이렇게 사용법과 GetWorldTimerManager() 간단히 알아보았다. 그러나 GetWorldTimerManager() 를 사용할때 주의 사항과 GetWorld-> 를 이용해 GetWorldTimerManager() 를 호출해야 하는 경우를 알아보고 마치도록 하겠다.
GetWorldTimerManager() 주의 사항
1. 유효한 월드인지 확인해라. : GetWorldTimerManager() 는 현재 월드의 타이머 관리자를 반환한다. 따라서 이 함수를 호출 하기 전 월드가 유효한지 확인해주는 작업을 하면 좋다. 월드가 유효하지 않은 경우 (게임 시작 전, 월드 로딩 중 등) GetWorldTimerManager() 는 null 을 반환하기에 오류가 날 가능성이 있다.
2. 액터 컨텍스트: GetWorldTimerManager() 는 액터 또는 컴포넌트의 컨텍스트에서 호출해야 한다. 즉, 액터 또는 컴포넌트의 멤버 함수 내에서 GetWorldTimerManager()를 호출해야 한다는 의미이다. 이를 어길시 컴파일 오류가 발생할 가능성이 있다.
3. 타이머 핸들: 타이머를 설정할 때에는 꼭 FTimeHandle 을 사용하여 타이머를 관리해야 한다. FTimeHandle 이 타이머를 해제하거나 특정 타이머의 정보를 얻을 때 사용되기 때문이다.
4. 메모리 누수: 이것은 자원 관리와 비슷한 영역인데 타이머를 반복 실행하는 경우, 메모리 누수의 위험이 있기에 타이머를 적절하게 제어해야한다. 액터가 파괴될 때 타이머는 자동으로 해제되나, 액터가 오랫동안 존재하거나 반복적으로 생성/파괴 되는 경우에는 명시적으로 타이머를 해제하는 습관을 들이자.
GetWorld()->GetWorldTimerManager()
가끔 우리는 GetWorld()를 이용해 월드 타이머 매니저를 호출해야 할 때가 있다. 기본적으로 GetWorldTimerManager() 는 액터 또는 컴포넌트의 컨텍스트에서 직접 호출하여 현재 월드의 타이머 관리자를 반환한다. 그러나 월드 정보가 필요한 경우나 특정 월드의 타이머 관리자에 접근 해야 할 때 (여러 개의 월드가 존재하는 경우) GetWorld() 를 이용해 GetWorld()->GetWorldTimerManager() 를 사용해야한다.
월드 정보가 필요한 경우는 월드 객체 자체에 접근하여 월드에 대한 추가 정보를 얻어야 하는 경우이다. 예를 들어 월드의 레벨 이름, 액터 목록 등을 얻어야 하는 경우 월드 객체에 접근해야 한다. 또한 특정 월드의 타이머 관리자에 접근 해야하는 경우는 위에서 말했듯이 여러 개의 월드가 존재하는 경우, 특정 월드의 타이머 관리자에 접근하기 위해서 GetWorld() 함수를 사용하여 원하는 월드 객체를 가져온 후 GetWorldTimerManager() 를 호출해야한다.
오늘은 GetWorldTimerManager() 에 대해 알아보았다. C++ 로 사용할 때 흐름 제어를 위한 필수적인 요소이며 다루기 생각보다 어렵기에 기초를 알고 접근하는 것이 좋다고 생각하여 작성하게 되었다. 미약하게나마 도움이 되기를 원하며 오늘은 여기까지 작성하도록 하겠다.
'Unreal' 카테고리의 다른 글
보스 비헤이비어 트리 및 코드 - UnrealEngine (0) | 2025.03.05 |
---|---|
BehaviorTree 와 BlackBoard - UnrealEngine (0) | 2025.02.25 |
데이터 테이블 - Unreal Engine (0) | 2025.02.11 |
AnimationBlueprint 를 이용해 캐릭터의 움직임을 표현해보기 - Unreal Engine (0) | 2025.02.06 |
C++로 3인칭 캐릭터 움직여보기 (3) - Unreal Engine (1) | 2025.02.03 |