ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Unreal Engine] SetTimer() : C++ & Blueprint
    Unreal Engine 5._ 2023. 1. 19. 15:06

    게임을 만들다보면, 시간에 관계되는 이벤트를 만들어야 되는 상황이 많이 발생합니다.

    개발에 정답은 없는만큼, 이러한 이벤트를 구현하는 방법 또한 여러가지가 될 수 있습니다만, 언리얼 엔진에서는 이러한 상황에 사용할 수 있는 라이브러리를 마련해 놓았습니다.

     

    이번 글에서는 언리얼 엔진에서 제공하는 SetTimer에 대하여 알아보도록 하겠습니다.

     


     

    1. SetTimer : Blueprint

     

    더보기

    Bluepirnt에서 제공하는 SetTimer는 그 용도에 따라 4종류가 존재합니다.

    SetTimer 관련 액션

    위 4개의 액션은, 다시 두 갈래로 묶을 수 있습니다.

     

    첫 번째는 반복여부, 반복 간 시간 등을 사용자가 자유롭게 설정할 수 있는 액션입니다.

    각각의 핀에 대해서 살펴보도록 하겠습니다.

    • Object 
      • Function Name에 있는 핀 입니다. 해당 Function Name을 지닌 오브젝트를 요구합니다.
    • Function Name / Event
      • 실행시킬 함수, 이벤트를 요구하는 핀 입니다.
    • Time
      • 반복실행 사이의 유휴시간을 의미합니다. 시작 전 시간에도 포함됩니다.
    • Looping
      • 실행의 반복 여부를 선택하는 핀 입니다.
    • Initial Start Delay
      • 실행 전 대기 시간을 지정하는 핀 입니다.
    • Initial Start Delay Variance
      • 실행 전 대기 시간을 지정하는 핀 입니다. 0에서 지정한 값 사이의 랜덤한 수치가 딜레이에 추가됩니다.

     

    두 번째는 지정한 함수, 이벤트를 다음 틱에 실행시키는 액션입니다.

    해당 액션이 호출되면, 지정한 함수 혹은 이벤트가 다음 틱에 실행됩니다.

    정밀도가 높아야 하는 게임은 프레임 단위로 연산을 해야하는 경우가 매우 많으므로, 그러한 상황을 구현할 때 유용할 것 입니다.

    입력으로 받는 핀은 첫 번째 액션에서 설명한 핀과 중복되므로 생략하도록 하겠습니다.

     

    2. Timer Handle Structure : Blueprint

     

    더보기

    SetTimer를 통해 Timer를 등록하면, TimerHandle 구조체를 반환합니다.

    이 구조체는 다음과 같은 액션을 호출하는 데 사용됩니다.

    SetTimer의 Return값인 Timer Handle Structure 핀에서 이어지는 액션

    이 구조체는 설정된 Timer에 대한 정보를 가지고 있습니다.

    위 액션들을 통해 설정된 Timer를 초기화, 일시정지, 재실행 등등 Timer의 동작과 관련된 행동을 취할 수 있습니다.

     

    3. SetTimer : C++

     

    더보기

    C++의 SetTimer 또한 Blueprint와 같이 두 가지 종류가 있습니다.

    시간과 반복 여부를 정하는 SetTimer와, 다음 틱에 실행시키는 SetTimerForNextTick입니다.

     

    위 함수들은 FTimerManager의 멤버 함수입니다.

    FTimerManager는 UWorld의 멤버 변수로 존재합니다.

    따라서, SetTimer함수는 아래와 같은 형태로 호출이 가능합니다.

    // Set Timer
    GetWorld()->GetTimerManager().SetTimer( ... );
    
    // Set Timer for Next Tick
    GetWorld()->GetTimerManager().SetTimerForNextTick( ... );

     

    Blueprint의 함수, 이벤트는 C++에서는 구분없이 함수로 취급됩니다.

    다만 람다라는 예외가 있어, C++의 SetTimer의 인자로 넘겨주는 함수는 함수 포인터, 혹은 람다의 형태가 됩니다.

     

    4. SetTimer : C++ : SetTimerForNextTick

     

    더보기

    Timer함수에 행동을 정의하는 방법은 여러가지가 있습니다.

    이번 문단에서는 두 가지 Timer중 SetTimerForNextTick에 대해 살펴볼 것 입니다.

    해당 함수의 오버로딩 버전에 대한 설명은 언리얼 공식 문서에도 있습니다.

    SetTimerForNextTick | Unreal Engine Documentation

     

    첫 번째로, 함수를 지정하는 방법입니다.

    SetTimerForNextTick(UserClass* inObj, FTimerDelegate::TUObjectMethd<UserClass> inTimerMethod)

    GetWorld()->GetTimerManager().SetTimerForNextTick(
        this,
        &AMyActor::Func1
    );

    첫 번째 인자는 UserClass* 로, 두 번째 인자의 함수를 가지고 있는 클래스의 포인터를 지정합니다.

    두 번째 인자는 해당 클래스가 가진 함수의 참조입니다. (예제는 AMyActor클래스의 Func1함수를 의미합니다.)

     

    두 번째는 람다를 이용하는 것 입니다.

    SetTimerForNextTick(TFunction<void>&& Callback)

    GetWorld()->GetTimerManager().SetTimerForNextTick([]() {
            // Function body
        }
    );

    일반적인 람다를 사용하듯 사용 가능합니다.

     

    다음은 FTimerDelegate클래스를 이용하는 예제입니다.

    세 번째는 FTimerDelegate의 CreateLambda 함수를 이용하는 방법입니다.

    SetTimerForNextTick(const FTimerDelegate &InDelegate)

    GetWorld()->GetTimerManager().SetTimerForNextTick(
        FTimerDelegate::CreateLambda([]() {
            // Function Body
    	})
    );

    이것은 두 번째의 람다와 매우 유사하지만, CreateLambda함수가 FTimerDelegate를 반환하는 특징이 있습니다. 이에 관해서는 다른 글에서 다룰 수 있도록 하겠습니다.

     

    추가적으로, FTimerDelegate인스턴스를 직접 등록하는 방법도 있습니다.

    FTimerDelegate myDelegate;
    // Way 1
    myDelegate.BindLambda([]() {
        // Function Body
    });
    
    // Way 2
    myDelegate.BindUObject(this, &AMyActor::Func1);
    
    // Way 3
    myDelegate.BindUFunction(this, FName("Func1"));
    
    // Usage
    GetWorld()->GetTimerManager().SetTimerForNextTick(myDelegate);

    FTimerDelegate를 선언한 후, 적절한 방법으로 함수를 등록합니다.

    이후 SetTimerForNextTick의 인자로 FTimerDelegate를 넘겨주면 완성됩니다.

     

    FTimerDelegate의 CreateLambda함수와 FTimerDelegate인스턴스를 직접 등록하는 방법은 모두 같은 오버로딩 버전(SetTimerForNextTick(const FTimerDelegate &InDelegate))을 호출했기 때문에, 같이 서술했습니다.

     

    마지막 방법은 FTimerDynamicDelegate를 이용하는 방법입니다.

    SetTimerForNextTick(const FTimerDynamicDelegate &InDynDelegate);

    FTimerDynamicDelegate myDynDelegate;
    myDynDelegate.BindUFunction(this, FName("Func1"));
    
    GetWorld()->GetTimerManager().SetTimerForNextTick(myDynDelegate);

    FTimerDelegate와 FTimerDynamicDelegate의 차이점은 차후 다른 글에서 정리할 수 있도록 하겠습니다.

    현재까지 보이는 차이점은 등록하는 방법의 가짓수 입니다.

    FTimerDynamicDelegate는 BindUFunction외에 다른 Bind함수를 지원하지 않습니다.

     

    5. SetTimer : C++ : SetTimer

     

    더보기

    문단의 제목이 신경쓰이지만, 위 문단에서 SetTimerForNextTick에 대하여 살펴보았고, 이번 문단에서는 SetTimer에 대하여 살펴 볼 예정이라 위와 같이 지었습니다.

    이번 문단에서 살펴볼 SetTimer의 오버로딩 버전에 대한 내용은 언리얼 엔진 공식 문서에도 있습니다.

    SetTimer | Unreal Engine Documentation

     

    전체적으로, SetTimer함수들의 오버로딩 버전은 첫 번째 인자로 FTimerHandle인스턴스를 요구합니다.

    이 인스턴스에 대한 설명은 다음 문단에서 이어가도록 하겠습니다.

    오버로딩에 요구되는 인자가 많은 만큼, FTimerHandle인자는 제외하도록 하고, 다음 코드가 이미 선언되어 있다고 가정하도록 하겠습니다.

    FTimerHandle myHandle;

    (실제로는 모든 함수의 첫 번째 인자는 FTimerHandle입니다.)

     

    첫 번째로 살펴볼 함수는 Delegate를 받지 않는 버전입니다.

    SetTimer(, float InRate, bool InbLoop, float InFirstDelay = (1.0F))

    GetWorld()->GetTimerManager().SetTimer(
        myHandle,
        1.0f,
        true,
        1.0f
    );

    어떤 함수도 입력으로 받지 않기 때문에, 무언가가 실행되고 있는지 확인할 수 없습니다.

    추측이지만, 스트레스 테스트 등에 활용될 수 있지 않을까 합니다.

     

    SetTimer함수의 마지막 3개의 인자는 다음과 같습니다.

    • float InRate
      • 실행 주기를 의미하는 매개변수 입니다.
    • bool InbLoop
      • 반복 여부를 의미하는 매개변수 입니다.
    • float InFirstDelay = (1.0F)
      • 최초 시작 시점을 의미하는 매개변수 입니다. 기본값 1.0F가 주어져 있습니다.

    FTimerHandle과 동일하게, 위 3개의 매개변수 또한 이후 서술에 있어 생략하도록 하겠습니다.

     

    두 번째는 함수를 직접 지정하는 방법입니다.

    SetTimer(, UserClass* InObj, FSimpleDelegate::TMethodPtr<UserClass> InTimerMethod, , , )

    GetWorld()->GetTimerManager().SetTimer(
        myHandle,
        this,
        &AMyActor::Func1,
        1.0f,
        true,
        1.0f
    );

     

    세 번째는 람다를 직접 사용하는 방법입니다.

    SetTimer(, TFunction<void>&& Callback, , , )

    GetWorld()->GetTimerManager().SetTimer(
        myHandle,
        [](){
            // Function body
        },
        1.0f,
        true,
        1.0f
    );

     

    네 번째는 FTimerDelegate를 이용하는 방법입니다.

    SetTimer(, const FTimerDelegate &InDelegate, , , )

    GetWorld()->GetTimerManager().SetTimer(
        myHandle,
        FTimerDelegate::CreateLambda([](){
            // Function body
        }),
        1.0f,
        true,
        1.0f
    );

     위와 같이 CreateLambda함수를 이용할 수 있고, 아래와 같이 FTimerDelegate를 직접 등록하는 방법도 있습니다.

    FTimerDelegate myDelegate;
    // Way 1
    myDelegate.BindLambda([]() {
        // Function Body
    });
    
    // Way 2
    myDelegate.BindUObject(this, &AMyActor::Func1);
    
    // Way 3
    myDelegate.BindUFunction(this, FName("Func1"));
    
    // Usage
    GetWorld()->GetTimerManager().SetTimer(
        myHandle,
        myDelegate,
        1.0f,
        true,
        1.0f
    );

     

    다섯 번째 방법은 FTimerDynamicDelegate를 이용하는 방법입니다.

    SetTimer(, const FTimerDynamicDelegate &InDynDelegate, , , )

    FTimerDynamicDelegate myDynDelegate;
    myDynDelegate.BindUFunction(this, FName("Func1"));
    
    GetWorld()->GetTimerManager().SetTimer(
        myHandle,
        myDynDelegate,
        1.0f,
        true,
        1.0f
    );

     

    전체적으로 SetTimerForNextTick과 유사하지만, 실행주기, 반복여부, 최초 실행 시간 등의 제어 여부를 선택할 수 있다는 점과, FTimerHandle객체가 필요하다는 점에서 차이가 존재합니다.

     

    6. FTimerHandle

     

    더보기

    SetTimer의 Blueprint버전은 Timer Handle Struct를 반환합니다.

    C++의 SetTimer 또한 FTimerHandle객체를 이용하지만, 결과를 반환하는 방법이 다릅니다.

    • SetTimer함수들은 인자에 FTimerHandle참조를 요구하며, 결과 또한 이 인스턴스에 저장됩니다.
    • SetTimerForNextTick함수들은 반환형이 FTimerHandle입니다.

    이 FTimerHandle객체는 자체적인 IsValid함수를 가지고 있습니다.

    FTimerHandle myHandle
    
    if(myHandle.IsValid()) { }

    위와 같은 방식으로 핸들의 유효성을 판단할 수 있습니다.

     

    그 외에도, FTimerManager에 FTimerHandle을 이용하는 여러 함수가 존재합니다.

    GetWorld()->GetTimerManager().ClearTimer(myHandle);
    GetWorld()->GetTimerManager().GetTimerRate(myHandle);
    GetWorld()->GetTimerManager().GetTimerElapsed(myHandle);
    GetWorld()->GetTimerManager().PauseTimer(myHandle);
    GetWorld()->GetTimerManager().UnPauseTimer(myHandle);
    • ClearTimer
      • Timer를 초기화시킵니다. (동작이 멈추고, 설정된 타이머가 해제됩니다.)
    • GetTimerRate
      • 다음 동작까지의 남은 시간을 반환합니다. (float)
    • GetTimerElapsed
      • 타이머의 경과 시간을 반환합니다. (float)
    • PauseTimer
      • 타이머를 일시정지합니다.
    • UnPauseTimer
      • 타이머의 동작을 재개합니다.

    이 외에도, FTimerHandle을 이용해서 할 수 있는 여러가지 조작을 제공합니다.

     


     

    언리얼 엔진의 SetTimer에 대하여 알아보았습니다.

    위 기능을 활용한다면 특정 시간에 동작하는 기능을 만들거나, 특정 시간동안 작동하는 기능들을 만드는 등

    여러가지 시간에 관련된 기능들을 만들 수 있습니다.

    예를 들면 게임의 DOT기능이나, 스킬 쿨타임 등의 기능이 있겠습니다.

     

    이 글이 도움이 되셨기를 바랍니다.

    감사합니다.

    댓글

Designed by Tistory.