인터페이스에서 BlueprintNativeEvent 함수와 기본 UFUNCTION 함수의 차이점
BlueprintNativeEvent는 Unreal Engine에서 C++과 블루프린트 간의 상호작용을 위해 제공하는 중요한 기능입니다. 주로 C++에서 함수를 정의하고, 해당 함수를 블루프린트에서도 오버라이드하여 사용할 수 있게 할 때 사용됩니다. 이 기능은 C++에서 기본 구현을 제공하면서도, 블루프린트에서 추가적으로 함수 로직을 확장하거나 오버라이드하는 것이 가능하다는 장점이 있습니다.
1. BlueprintNativeEvent의 기본 개념
- C++ 기본 구현: C++에서 기본 구현을 제공할 수 있으며, 이 기본 구현을 블루프린트에서도 호출할 수 있습니다.
- 블루프린트 오버라이드: 블루프린트에서 이 함수를 오버라이드하여 C++에서 제공된 기본 구현을 덮어쓸 수 있습니다.
- 유연성: 블루프린트로 프로젝트를 확장할 때, C++ 로직을 유지하면서 블루프린트로도 자유롭게 상호작용할 수 있는 구조를 제공합니다.
2. BlueprintNativeEvent의 사용 방법
2.1. 함수 선언
C++에서 BlueprintNativeEvent 로 함수를 선언할 때는 UFUNCTION 매크로를 사용하여 지정합니다. 중요한 것은, 이 함수는 두 가지로 나뉩니다:
- _Implementation 함수: C++에서 기본 로직을 작성하는 함수.
- 원래 함수: 블루프린트에서 오버라이드할 함수.
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" 탭에서 이 함수를 선택한 후 새로운 구현을 작성할 수 있습니다.
- 블루프린트 에디터에서 해당 클래스의 블루프린트 열기.
- Functions 탭에서 OnItemPickedUp 함수를 찾아서 클릭.
- 오버라이드한 후 블루프린트 노드에서 로직을 추가.
이렇게 하면 블루프린트에서 C++ 기본 구현을 덮어쓰고 자신만의 로직을 추가할 수 있습니다.
3. BlueprintNativeEvent와 BlueprintImplementableEvent의 차이점
BlueprintNativeEvent와 비슷한 기능으로 BlueprintImplementableEvent가 있는데 둘 사이에는 중요한 차이점이 있습니다.
BlueprintNativeEventBlueprintImplementableEventC++ 기본 구현 | 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를 사용해야 하는가?
- 리플렉션 및 블루프린트 지원: BlueprintNativeEvent는 블루프린트와의 상호작용을 위한 특수 매크로로, 함수가 블루프린트에서 오버라이드될 수 있도록 지원합니다. 이를 통해 C++ 코드와 블루프린트 코드 간의 상호작용이 가능해집니다.
- Execute 함수 생성: BlueprintNativeEvent로 선언하면 Unreal Engine이 자동으로 Execute_{FunctionName} 함수를 생성합니다. 이를 통해 인터페이스를 구현한 객체에서 해당 함수가 실행되도록 보장됩니다.
- 가상 함수 구현 및 오버라이드: 이 매크로는 가상 함수로 선언되기 때문에, C++에서 기본 구현을 제공할 수 있으며, 블루프린트에서 이를 오버라이드하여 새로운 동작을 정의할 수 있습니다.
UFUNCTION(BlueprintNativeEvent) 로 선언된 interface 함수와 UFUNCTION() 으로 선언된 interface 함수의 호출방식 차이점
실행방식 :
UFUNCTION() | UFUNCTION( BlueprintNativeEvent) | |
호출 방식 | PlayerInterface->AddToItem() | IPlayerInterface::Execute_AddToItem() |
호출 방식 | 직접 호출 | 간접 호출 |
성능 | 더 빠를 수 있음 (추가 확인 절차 없음) | 약간 느림 (간접 호출 + 추가적인 객체 확인 로직) |
Blueprint 지원 | 지원하지 않음 (C++ 기본 구현만 호출) | Blueprint 오버라이드도 호출 가능 |
호출 로직 | 객체가 인터페이스를 구현하면 바로 함수 호출 | Unreal이 생성한 Execute 함수를 통해 인터페이스 구현을 확인 |
실행 흐름 | C++에서 구현된 함수 호출 | 블루프린트 또는 C++에서 오버라이드된 함수 호출 가능 |
언제 각각의 방식을 사용해야 하나?
- PlayerInterface->AddToItem() 방식:
- 성능이 중요한 상황에서 블루프린트 오버라이드가 필요 없는 경우.
- C++에서만 동작하며, 블루프린트에서 추가적인 동작을 제공할 필요가 없는 함수.
- 인터페이스의 C++ 기본 구현만 사용하고 싶을 때.
- 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 함수는 이러한 객체 상태 확인을 자동으로 처리하므로, 호출자 측에서 추가적인 확인 로직이 필요 없습니다.