UE UFUNCTION(BlueprintNativeEvent)
BlueprintNativeEvent : would have the C++ and Blueprint version.
BlueprintImplementableEvent : only has a Blueprint version.
BlueprintCallable : create function which is callable in Blueprint.
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++에서의 함수 호출은 자유롭다.
댓글
댓글 쓰기