Unreal Engine

인터페이스에서 BlueprintNativeEvent 함수와 기본 UFUNCTION 함수의 차이점

띠애모 2024. 10. 8. 15:00

BlueprintNativeEvent는 Unreal Engine에서 C++과 블루프린트 간의 상호작용을 위해 제공하는 중요한 기능입니다. 주로 C++에서 함수를 정의하고, 해당 함수를 블루프린트에서도 오버라이드하여 사용할 수 있게 할 때 사용됩니다. 이 기능은 C++에서 기본 구현을 제공하면서도, 블루프린트에서 추가적으로 함수 로직을 확장하거나 오버라이드하는 것이 가능하다는 장점이 있습니다.

1. BlueprintNativeEvent의 기본 개념

  • C++ 기본 구현: C++에서 기본 구현을 제공할 수 있으며, 이 기본 구현을 블루프린트에서도 호출할 수 있습니다.
  • 블루프린트 오버라이드: 블루프린트에서 이 함수를 오버라이드하여 C++에서 제공된 기본 구현을 덮어쓸 수 있습니다.
  • 유연성: 블루프린트로 프로젝트를 확장할 때, C++ 로직을 유지하면서 블루프린트로도 자유롭게 상호작용할 수 있는 구조를 제공합니다.

2. BlueprintNativeEvent의 사용 방법

2.1. 함수 선언

C++에서 BlueprintNativeEvent 로 함수를 선언할 때는 UFUNCTION 매크로를 사용하여 지정합니다. 중요한 것은, 이 함수는 두 가지로 나뉩니다:

  1. _Implementation 함수: C++에서 기본 로직을 작성하는 함수.
  2. 원래 함수: 블루프린트에서 오버라이드할 함수.
1
2
UFUNCTION(BlueprintNativeEvent, Category = "Item")
void OnItemPickedUp();
cs
  • BlueprintNativeEvent: 이 매크로는 이 함수가 블루프린트에서 오버라이드될 수 있음을 나타냅니다.
  • Category: 블루프린트에서 함수가 어떤 카테고리에 포함될지 지정합니다.

2.2. 함수 구현

_Implementation 접미사를 붙여 기본 C++ 구현을 제공합니다. 이 함수는 C++에서 호출할 때 실행되며, 블루프린트에서 오버라이드하지 않는 한 블루프린트에서도 이 기본 구현이 실행됩니다.

 

1
2
3
4
void AMyCharacter::OnItemPickedUp_Implementation()
{
    UE_LOG(LogTemp, Warning, TEXT("Item picked up in C++"));
}
cs

 

2.3. 블루프린트에서 오버라이드

블루프린트에서 이 함수를 오버라이드하려면 해당 함수가 선언된 클래스를 기반으로 한 블루프린트에서 "Functions" 탭에서 이 함수를 선택한 후 새로운 구현을 작성할 수 있습니다.

  1. 블루프린트 에디터에서 해당 클래스의 블루프린트 열기.
  2. Functions 탭에서 OnItemPickedUp 함수를 찾아서 클릭.
  3. 오버라이드한 후 블루프린트 노드에서 로직을 추가.

이렇게 하면 블루프린트에서 C++ 기본 구현을 덮어쓰고 자신만의 로직을 추가할 수 있습니다.

3. BlueprintNativeEvent와 BlueprintImplementableEvent의 차이점

BlueprintNativeEvent와 비슷한 기능으로 BlueprintImplementableEvent가 있는데 둘 사이에는 중요한 차이점이 있습니다.

BlueprintNativeEventBlueprintImplementableEvent
C++ 기본 구현 C++에서 기본 구현을 제공할 수 있음 (_Implementation 함수) C++에서는 기본 구현을 제공할 수 없으며, 반드시 블루프린트에서 구현해야 함
블루프린트 오버라이드 블루프린트에서 오버라이드할 수 있음 블루프린트에서만 구현 가능
사용 시기 C++에서 기본 동작을 제공하면서, 블루프린트에서 필요할 때만 오버라이드 모든 로직을 블루프린트에서 처리할 때
  • BlueprintNativeEvent: C++에서 기본 구현을 제공하고 싶을 때 사용합니다. 블루프린트에서 이를 오버라이드하지 않으면 기본 C++ 구현이 실행됩니다.
  • BlueprintImplementableEvent: C++에서 기본 구현이 필요 없고 블루프린트에서만 구현해야 할 때 사용합니다.

4. BlueprintNativeEvent 호출 방법

BlueprintNativeEvent로 선언된 함수는 C++이나 블루프린트에서 호출할 수 있으며, 블루프린트에서 오버라이드한 함수가 호출됩니다. 만약 블루프린트에서 오버라이드하지 않은 경우 C++ 기본 구현이 호출됩니다.

1
2
3
4
5
void AMyCharacter::SomeFunction()
{
    // OnItemPickedUp 호출 - 블루프린트에서 오버라이드했으면 그 구현이 실행됨
    OnItemPickedUp();
}
cs

 

이렇게 호출하면 블루프린트에서 오버라이드된 함수가 있으면 그 로직이 실행되고 없으면 C++ 기본 구현이 실행됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
UCLASS()
class MYGAME_API AMyCharacter : public ACharacter
{
    GENERATED_BODY()
 
public:
    // BlueprintNativeEvent 선언
    UFUNCTION(BlueprintNativeEvent, Category = "Item")
    void OnItemPickedUp();
 
    // 기본 C++ 구현
    virtual void OnItemPickedUp_Implementation();
};
cs

 

interface 함수에 UFUNCTION(BlueprintNativeEvent) 를 추가하는 이유

 

인터페이스 함수에 UFUNCTION(BlueprintNativeEvent)를 추가하는 이유는, C++과 블루프린트 간의 상호작용을 가능하게 하면서 인터페이스를 유연하게 확장할 수 있도록 하기 위함입니다. 이 방법을 사용하면 인터페이스 함수가 C++과 블루프린트 모두에서 구현될 수 있으며 이를 통해 블루프린트에서 해당 인터페이스 함수를 쉽게 오버라이드할 수 있습니다.

1. BlueprintNativeEvent와 인터페이스의 결합

  • 인터페이스(Interface)는 객체가 특정 기능을 제공하기 위해 구현해야 하는 함수들의 집합입니다. Unreal Engine에서는 인터페이스를 통해 다양한 클래스 간의 통신을 처리할 수 있습니다.
  • BlueprintNativeEvent는 인터페이스 함수가 C++에서 기본 구현을 제공하면서 블루프린트에서도 오버라이드할 수 있는 기능을 제공합니다.

인터페이스에 BlueprintNativeEvent를 사용하면 해당 함수를 블루프린트에서 구현할 수 있을 뿐만 아니라 C++에서도 기본 구현을 제공하거나 추가적인 처리를 할 수 있습니다.

2. 인터페이스 함수에서 BlueprintNativeEvent를 사용하는 이유

2.1. C++ 기본 구현 제공

BlueprintNativeEvent로 선언된 인터페이스 함수는 C++에서 기본 구현을 제공할 수 있습니다. 즉 인터페이스 함수를 정의할 때 C++ 기본 동작을 정의할 수 있고 필요에 따라 블루프린트에서 오버라이드할 수 있습니다. 이를 통해 코드의 재사용성과 유연성을 높일 수 있습니다.

2.2. 블루프린트에서 오버라이드 가능

BlueprintNativeEvent를 사용하면 블루프린트에서도 이 인터페이스 함수를 구현할 수 있습니다. 인터페이스가 적용된 클래스는 블루프린트에서 해당 인터페이스의 함수를 오버라이드하여 자신의 로직을 추가할 수 있습니다.

예를 들어 여러 클래스가 같은 인터페이스를 구현하더라도 각각의 블루프린트에서 개별적인 동작을 정의할 수 있게 됩니다.

2.3. 다양한 클래스에서 유연한 구현 가능

BlueprintNativeEvent로 정의된 인터페이스 함수는 C++ 코드블루프린트에서 모두 구현될 수 있습니다. 이를 통해 다양한 클래스에서 동일한 인터페이스를 상속받아 각 클래스의 특성에 맞게 C++ 기본 구현이나 블루프린트 오버라이드로 다양한 동작을 수행할 수 있습니다.

 

 

UFUNCTION(BlueprintNativeEvent) 로 선언된 interface 함수와 UFUNCTION() 으로 선언된 interface 함수의 차이점

1. UFUNCTION()만 사용한 경우 (기본 가상 함수)

1
2
UFUNCTION()
virtual void AddToItem(FGameplayTag ItemTag);
cs

 

  • 설명: 이 함수는 Unreal Engine의 리플렉션 시스템을 사용하여 블루프린트 또는 다른 Unreal 기능에서 사용할 수 있도록 하는 가상 함수입니다. 하지만, 이 함수는 인터페이스에서 직접 호출할 수 없습니다. UFUNCTION()만 사용한 경우, Unreal Engine의 리플렉션 시스템을 통해 해당 함수가 노출되긴 하지만, Execute_AddToItem 같은 인터페이스 실행 함수를 통해 호출될 수 있는 구조는 아닙니다.
  •  
  • 결과: IPlayerInterface::Execute_AddToItem와 같은 인터페이스 실행 함수는 자동 생성되지 않기 때문에, 인터페이스를 구현한 클래스의 함수를 직접 호출해야 합니다.

 

 

2. BlueprintNativeEvent로 선언한 경우

1
2
UFUNCTION(BlueprintNativeEvent)
void AddToItem(FGameplayTag ItemTag);
cs
  • BlueprintNativeEvent로 선언된 함수는 Unreal Engine의 리플렉션 시스템블루프린트 통합을 위해 사용됩니다.
  • 이를 사용하면 Unreal Engine은 자동으로 "Execute_{FunctionName}" 형식의 함수를 생성합니다. 이 함수는 인터페이스의 실제 구현 객체를 확인하고, 만약 해당 객체가 인터페이스를 구현하고 있으면, 그 객체의 함수를 호출하게 됩니다.

 

 

왜 BlueprintNativeEvent를 사용해야 하는가?

  1. 리플렉션 및 블루프린트 지원: BlueprintNativeEvent는 블루프린트와의 상호작용을 위한 특수 매크로로, 함수가 블루프린트에서 오버라이드될 수 있도록 지원합니다. 이를 통해 C++ 코드블루프린트 코드 간의 상호작용이 가능해집니다.
  2. Execute 함수 생성: BlueprintNativeEvent로 선언하면 Unreal Engine이 자동으로 Execute_{FunctionName} 함수를 생성합니다. 이를 통해 인터페이스를 구현한 객체에서 해당 함수가 실행되도록 보장됩니다.
  3. 가상 함수 구현 및 오버라이드: 이 매크로는 가상 함수로 선언되기 때문에, C++에서 기본 구현을 제공할 수 있으며, 블루프린트에서 이를 오버라이드하여 새로운 동작을 정의할 수 있습니다.

UFUNCTION(BlueprintNativeEvent) 로 선언된 interface 함수와 UFUNCTION() 으로 선언된 interface 함수의 호출방식 차이점

                                           
실행방식 :  

  UFUNCTION()       UFUNCTION( BlueprintNativeEvent
호출 방식 PlayerInterface->AddToItem()   IPlayerInterface::Execute_AddToItem()
호출 방식 직접 호출 간접 호출
성능 더 빠를 수 있음 (추가 확인 절차 없음) 약간 느림 (간접 호출 + 추가적인 객체 확인 로직)
Blueprint 지원 지원하지 않음 (C++ 기본 구현만 호출) Blueprint 오버라이드도 호출 가능
호출 로직 객체가 인터페이스를 구현하면 바로 함수 호출 Unreal이 생성한 Execute 함수를 통해 인터페이스 구현을 확인
실행 흐름 C++에서 구현된 함수 호출 블루프린트 또는 C++에서 오버라이드된 함수 호출 가능

 

언제 각각의 방식을 사용해야 하나?

  1. PlayerInterface->AddToItem() 방식:
    • 성능이 중요한 상황에서 블루프린트 오버라이드가 필요 없는 경우.
    • C++에서만 동작하며, 블루프린트에서 추가적인 동작을 제공할 필요가 없는 함수.
    • 인터페이스의 C++ 기본 구현만 사용하고 싶을 때.
  2. IPlayerInterface::Execute_AddToItem() 방식:
    • 블루프린트에서 해당 함수를 오버라이드할 수 있는 가능성이 있을 때.
    • 게임플레이에서 C++과 블루프린트 간의 상호작용을 지원해야 할 때.
    • 블루프린트에서 로직이 변경될 수 있는 경우, 또는 유연한 함수 호출이 필요한 경우.

결론

  • PlayerInterface->AddToItem() 방식은 직접 호출을 통해 성능 면에서 이점이 있을 수 있지만, 블루프린트에서 오버라이드된 함수는 호출되지 않습니다.
  • IPlayerInterface::Execute_AddToItem() 방식은 간접 호출을 통해 블루프린트에서 오버라이드된 구현까지도 실행할 수 있습니다. 따라서, 유연하게 사용할 수 있지만, 성능 측면에서는 약간의 오버헤드가 발생할 수 있습니다.

IPlayerInterface::Execute_AddToItem()와 같은 간접 호출 방식은 다음과 같은 이유로 더 안전하고 유연한 방식입니다:

  • 블루프린트 오버라이드 지원: 블루프린트에서 구현된 함수까지 호출 가능하여 확장성을 보장.
  • 다형성 지원: 객체가 인터페이스를 구현했는지 확인 후 적절한 함수 호출.
  • 객체 상태 관리: 객체가 인터페이스를 구현하고 있지 않더라도 안전하게 처리.
  • 호환성: C++ 및 블루프린트에서 동일하게 동작하도록 보장.

안정적인 인터페이스 처리

  • 객체가 인터페이스를 구현하는지 확인: Execute 함수는 객체가 해당 인터페이스를 구현하고 있는지 먼저 확인한 후 함수를 호출합니다. 이를 통해 잘못된 함수 호출을 방지할 수 있습니다. 즉, Execute_AddToItem를 호출할 때 객체가 인터페이스를 구현하지 않았더라도 안전하게 처리되며, 강제적인 호출로 인한 크래시 가능성을 낮춥니다.

PlayerInterface->AddToItem()  같은 직접 호출방식은 더 단순한 방식이지만, 특정한 제약이 있습니다.

2.1. 블루프린트 오버라이드 무시

  • 오버라이드 미지원: 직접 호출 방식은 C++ 기본 구현만 호출할 수 있으며, 블루프린트에서 오버라이드된 함수는 호출할 수 없습니다. 이는 후에 블루프린트에서 기능을 확장하거나 변경하고 싶을 때 유연성이 떨어지고, 기능이 예상대로 동작하지 않을 수 있습니다.
  • 불안정한 코드 변경: 게임 개발이 진행됨에 따라 함수의 구현이 바뀌고 블루프린트에서 구현된 로직이 필요해질 때, 직접 호출 방식은 이러한 변경을 처리하지 못해 기능이 누락되거나 코드가 불안정해질 수 있습니다.

2.2. 객체 상태 확인 필요

  • 직접 호출 방식은 호출하려는 객체가 인터페이스를 구현하고 있는지 직접 확인해야 합니다. 이를 위한 Cast나 dynamic_cast 같은 추가적인 작업이 필요하며, 이러한 확인을 누락하거나 잘못 처리할 경우 프로그램이 예기치 않게 크래시할 가능성이 있습니다.
  • 반면에, Execute 함수는 이러한 객체 상태 확인을 자동으로 처리하므로, 호출자 측에서 추가적인 확인 로직이 필요 없습니다.