icon안동민 개발노트

타이머 및 틱 (Tick) 함수 활용


 언리얼 엔진에서 타이머와 틱 함수는 시간 기반 이벤트와 주기적인 업데이트를 처리하는 데 중요한 역할을 합니다.

 이 절에서는 이들의 개념과 C++에서의 활용 방법을 살펴보겠습니다.

타이머 시스템

 타이머의 종류

  1. 단일 실행 타이머
  2. 반복 실행 타이머
  3. 지연 실행 타이머

 타이머 설정 방법

// 단일 실행 타이머
FTimerHandle TimerHandle;
GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &AMyActor::MyTimerFunction, 2.0f, false);
 
// 반복 실행 타이머
GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &AMyActor::MyRepeatingFunction, 1.0f, true);
 
// 지연 실행 타이머
FTimerDelegate TimerDelegate;
TimerDelegate.BindUFunction(this, FName("MyDelayedFunction"), Param1, Param2);
GetWorld()->GetTimerManager().SetTimerForNextTick(TimerDelegate);

 타이머 관리 기법

// 타이머 일시 정지
GetWorld()->GetTimerManager().PauseTimer(TimerHandle);
 
// 타이머 재개
GetWorld()->GetTimerManager().UnPauseTimer(TimerHandle);
 
// 타이머 중지 및 제거
GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
 
// 남은 시간 확인
float RemainingTime = GetWorld()->GetTimerManager().GetTimerRemaining(TimerHandle);
 
// 타이머 활성 여부 확인
bool bIsActive = GetWorld()->GetTimerManager().IsTimerActive(TimerHandle);

틱(Tick) 함수

 틱 함수의 역할 틱 함수는 매 프레임마다 호출되어 지속적인 업데이트가 필요한 로직을 처리합니다.

 커스텀 틱 함수 구현

UCLASS()
class MYGAME_API AMyActor : public AActor
{
    GENERATED_BODY()
 
public:
    AMyActor();
 
    virtual void Tick(float DeltaTime) override;
 
private:
    float AccumulatedTime;
};
 
AMyActor::AMyActor()
{
    PrimaryActorTick.bCanEverTick = true;
    AccumulatedTime = 0.0f;
}
 
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
 
    AccumulatedTime += DeltaTime;
    if (AccumulatedTime >= 1.0f)
    {
        UE_LOG(LogTemp, Log, TEXT("One second has passed!"));
        AccumulatedTime -= 1.0f;
    }
}

 틱 함수의 성능 영향과 최적화 전략

  1. 조건부 틱 활성화
void AMyActor::SetShouldTick(bool bShouldTick)
{
    SetActorTickEnabled(bShouldTick);
}
  1. 틱 간격 조정
PrimaryActorTick.TickInterval = 0.1f; // 0.1초마다 틱
  1. 틱 기능 컴포넌트화
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class MYGAME_API UMyTickComponent : public UActorComponent
{
    GENERATED_BODY()
 
public:
    UMyTickComponent();
 
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};

타이머와 틱 함수를 활용한 게임플레이 로직 구현

 시간 기반 이벤트 처리

void AMyGameMode::StartRound()
{
    GetWorld()->GetTimerManager().SetTimer(RoundTimerHandle, this, &AMyGameMode::EndRound, RoundDuration, false);
}
 
void AMyGameMode::EndRound()
{
    // 라운드 종료 로직
    UE_LOG(LogTemp, Log, TEXT("Round Ended!"));
}

 프레임 독립적인 업데이트 구현

void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
 
    // 프레임 독립적인 이동
    FVector NewLocation = GetActorLocation() + (Velocity * DeltaTime);
    SetActorLocation(NewLocation);
}

타이머와 틱 함수 사용 시 주의사항

  1. 타이머 핸들 관리: 객체 소멸 시 활성 타이머 제거
void AMyActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    Super::EndPlay(EndPlayReason);
    GetWorld()->GetTimerManager().ClearAllTimersForObject(this);
}
  1. 틱 함수 과다 사용 주의 : 필요한 경우에만 틱 활성화
  2. 긴 실행 시간 작업 회피 : 틱 함수에서 무거운 연산 피하기

타이머와 틱 함수의 효과적인 조합

  1. 주기적인 상태 체크에 타이머 사용, 연속적인 업데이트에 틱 함수 사용
void AMyActor::BeginPlay()
{
    Super::BeginPlay();
    GetWorld()->GetTimerManager().SetTimer(StatusCheckTimerHandle, this, &AMyActor::CheckStatus, 5.0f, true);
}
 
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    UpdatePosition(DeltaTime);
}
 
void AMyActor::CheckStatus()
{
    // 5초마다 상태 체크
}
 
void AMyActor::UpdatePosition(float DeltaTime)
{
    // 매 프레임 위치 업데이트
}
  1. 타이머를 사용한 틱 함수 최적화
void AMyActor::BeginPlay()
{
    Super::BeginPlay();
    GetWorld()->GetTimerManager().SetTimer(TickTimerHandle, this, &AMyActor::CustomTick, 0.1f, true);
    SetActorTickEnabled(false); // 기본 틱 비활성화
}
 
void AMyActor::CustomTick()
{
    // 0.1초마다 실행되는 커스텀 틱 로직
}

Best Practices

  1. 타이머 함수 네이밍 규칙
void AMyActor::OnHealthRegenTick()
{
    // 'On' 접두사와 'Tick' 접미사 사용
}
  1. 틱 함수 내 프로파일링
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
 
    SCOPE_CYCLE_COUNTER(STAT_MyActorTick);
    // 틱 로직
}
  1. 타이머와 델리게이트 조합
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTimerEventSignature);
 
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnTimerEventSignature OnTimerEvent;
 
// 타이머 설정
GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateUObject(this, &AMyActor::OnTimerFired), 1.0f, false);
 
void AMyActor::OnTimerFired()
{
    OnTimerEvent.Broadcast();
}
  1. 조건부 틱 함수 최적화
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
 
    if (bShouldUpdate)
    {
        PerformUpdate(DeltaTime);
    }
}

 타이머와 틱 함수는 언리얼 엔진에서 시간 기반 이벤트와 주기적인 업데이트를 처리하는 강력한 도구입니다. 타이머는 지연 실행, 반복 실행 등 다양한 시나리오에 활용할 수 있으며, 틱 함수는 매 프레임 업데이트가 필요한 로직에 적합합니다. 그러나 이들을 효과적으로 사용하기 위해서는 성능 영향을 항상 고려해야 합니다.

 타이머는 정확한 시간 간격이 필요한 경우나 비교적 긴 주기의 이벤트에 적합하며, 틱 함수는 연속적이고 빈번한 업데이트에 사용됩니다. 두 메커니즘을 적절히 조합하여 사용하면, 효율적이면서도 정확한 시간 기반 게임플레이 로직을 구현할 수 있습니다.

 성능 최적화를 위해 불필요한 틱 함수 호출을 줄이고, 타이머의 정확한 관리와 해제를 신경 써야 합니다. 프레임 독립적인 업데이트 로직을 구현하여 다양한 하드웨어 환경에서 일관된 게임플레이를 제공하는 것도 중요합니다.

 마지막으로, 디버깅과 프로파일링 도구를 활용하여 타이머와 틱 함수의 성능 영향을 지속적으로 모니터링하고 최적화하는 것이 좋습니다. 이를 통해 원활하고 효율적인 게임 실행을 보장할 수 있습니다.