본문 바로가기

UE4

[UE4] 함수 및 UFUNCTION 주요 지정자

0. 서문

이 글을 읽기에 앞서, UE4의 Reflection에 대한 이해가 없다면, 꼭 아래 문서부터 읽도록 하자.
 
함수는 일반 C++ 함수와 UFUNCTION, 두 가지의 기본적 형태로 존재 가능하다.
UFUNCTION은 전용 문법이 있어, 함수 지정자를 통해 함수에 대한 부가 정보를 선언부에 지정할 수 있다.
UFUNCTION([specifier, specifier, ...], [meta(key=value, key=value, ...)])
ReturnType FunctionName([Parameter, Parameter, ...])
 
UE4의 함수 전반적인 내용과 모든 specifier, meta에 대한 내용은 아래 문서를 참고하기 바란다.
 
이후 문서엔 자주 사용되는 specifier 위주로만 조금 더 자세히 정리하였다.
(UFUNCTION specifier 열거값들은 ObjectMacro.h의 namespace UF에서 확인할 수 있다)
 

1. BlueprintPure

(const) getter의 성격에 해당하는 함수를 블루프린트 그래프에 노출시킬 때 사용하면 된다.
// Return whether or not the pickup is active
UFUNCTION(BlueprintPure, Category = "Pickup")
bool IsActive() const { return bIsActive; }
 

2. BlueprintCallable

블루프린트 그래프에서 실행 가능한 가장 일반적인 specifier.
C++의 구현 내용을 블루프린트에서 재정의할 수 없으며, 호출만 가능하기에 대체적으로 setter 성격의 함수에 많이 사용된다.
// Set the pickup active or not
UFUNCTION(BlueprintCallable, Category = "Pickup")
void SetActive(bool active) { bIsActive = active; }
 

3. BlueprintNativeEvent

C++ 네이티브 구현이 존재하지만, 필요시 블루프린트에서 override 할 수 있게 해주는 지정자이다.
UHT는 해당 함수의 블루프린트 재지정이 존재하면 블루프린트의 그것을, 존재하지 않으면 C++의 함수가 호출되도록 처리해준다.
 
[FunctionName] 대신 [FunctionName]_Implementation 이라는 이름의 바디를 제공해 주고, 자동 생성 코드에는 필요할 때 구현 메쏘드를 호출하는 썽크가 포함된다.
 
예제부터 먼저 살펴보는 것이 나을 듯 하다.
// Pickup.h
UCLASS(Abstract)
class BATTERYCOLLECTOR_API APickup
: public AActor
{
   // ...

   // Declaration - 딱 이런 형식이다
   UFUNCTION(BlueprintNativeEvent)
   void OnCollection();
   virtual void OnCollection_Implementation();

   // ...
};

// BatteryPickup.h
UCLASS()
class BATTERYCOLLECTOR_API ABatteryPickup
: public APickup
{
    // ...

    // Declaration
    // OnCollection의 오버라이드
    // OnCollection 함수가 BlueprintNativeEvent이기에 Implementation을 오버라이드 한다
    // BlueprintNativeEvent 함수는 반드시 _Implementation suffix가 붙은 가상 함수를 추가해 줘야 한다.
    virtual void OnCollection_Implementation() override;

    // ...
};

// Pickup.cpp
// Definition : [FunctionName]_Implementation()만 정의
void APickup::OnCollection_Implementation()
{
    FString pickupDebugString = GetName();
    UE_LOG(LogClass, Log, TEXT("You have collected %s"), *pickupDebugString);
}

// BatteryPickup.cpp
// Definition : [FunctionName]_Implementation()만 정의
void ABatteryPickup::OnCollection_Implementation()
{
    Super::OnCollection_Implementation();

    Destroy();
}


// BatteryCollectorCharacter.cpp
void ABatteryCollectorCharacter::TryCollectPickups()
{
    // ...

    for (auto actor : collectedActor)
    {
        if (APickup* pickup = Cast<APickup>(actor))
        {
            // OnCollection_Implementation은 바디이고, 선언은 OnCollection으로 되었으니
            // 함수 호출은 선언된 시그너처를 사용하는 것이 당연지사.
            pickup->OnCollection();
        }
    }

    // ...
}
 
선언 형식이 조금 낯설텐데, 익숙해지는 방법 밖에는 없다.
 
예제에서 살펴본 형식을 정리하면 다음과 같다.
  • 선언부에 선언형식의 시그너쳐와 바디형식의 시그너쳐를 모두 작성한다.
  • 바디형식의 시그너쳐는 정확하게 "_Implementation" suffix를 가져야 한다.
  • 외부에서의 함수 호출시에는 선언형식의 시그너쳐를 따른다.
 

4. BlueprintImplementableEvent

 
이 지정자는 UHT(언리얼 헤더 툴)에게 블루프린트에 의해 구현될 빈 함수를 만들라고 알려준다.
즉, C++에서는 시그너쳐 선언만 하고 내용은 블루프린트에서만 구현 가능케 하는 함수 지정자이다.
UFUNCTION(BlueprintImplementableEvent, Category = Power)
void UpdatePowerEffect();
 
예제의 UpdatePowerEffect() 함수는 시그너쳐 선언만 C++에서 하고, 바디 구현은 블루프린트에서만 가능하다.
이후, C++에서의 함수 호출은 자유롭다.

'UE4' 카테고리의 다른 글

[UE4] World 내 Object/Actor 순회  (0) 2023.04.24
[UE4] MyLogMacro.h  (0) 2023.04.24
[UE4] 프로퍼티 및 UPROPERTY 주요 지정자  (0) 2023.04.24
[UE4] 클래스 및 UCLASS 주요 지정자  (0) 2023.04.24
[UE4] Garbage Collection overview  (0) 2023.04.24