클래스가 언리얼 인터페이스(Unreal Interface) 클래스에서 상속하면 인터페이스는 새 클래스가 공통의 함수 세트를 구현하도록 합니다. 이는 특정 함수 기능을 서로 다른 대규모의 복잡한 클래스에서 공유할 수 있을 때 유용합니다.
예를 들어 게임에 트리거 볼륨에 진입하는 플레이어 캐릭터가 함정을 발동하거나, 적에게 경보를 보내거나, 상황에 따라 플레이어에게 포인트를 줄 수 있는 시스템이 있다고 가정해 보겠습니다. 함정, 적 또는 포인트 지급에 ReactToTrigger 함수를 사용하여 이를 구현할 수 있습니다. 이 모든 활성 오브젝트가 ReactToTrigger 함수를 구현할 수 있지만 그 외에는 서로 다를 것입니다. 예시:
- 함정은
AActor에서 파생됩니다. - 적은
APawn또는ACharacter에서 파생됩니다. - 포인트 지급은
UDataAsset에서 파생됩니다.
이러한 클래스는 함수 기능을 공유하지만 기본 UObject 를 제외하면 공통된 부모가 없습니다. 이 경우 언리얼 인터페이스는 이 모든 오브젝트가 필요한 함수를 구현하도록 할 수 있습니다.
C++에서 인터페이스 선언하기
C++에서의 인터페이스 선언은 보통의 언리얼 클래스 선언과 비슷합니다. 하지만 다음과 같은 몇 가지 기본적인 차이가 있습니다.
- 인터페이스 클래스는
UCLASS매크로 대신UINTERFACE매크로를 사용합니다. - 인터페이스 클래스는
UObject대신UInterface에서 상속합니다.
UINTERFACE 클래스는 실제 인터페이스가 아니고 리플렉션 시스템에 비저빌리티를 제공하는 빈 클래스입니다.
C++ 클래스 마법사
언리얼 에디터에서 언리얼 인터페이스 클래스를 새로 생성하려면 다음 정보를 포함하여 C++ 클래스 마법사 문서의 단계를 따릅니다.
- 클래스: 언리얼 인터페이스
C++ 인터페이스 선언 예시
다음은 ReactToTriggerInterface 라는 C++ 인터페이스 선언의 예시입니다.
ReactToTriggerInterface.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "ReactToTriggerInterface.generated.h"
/*
이 클래스는 수정할 필요가 없습니다.
리플렉션 시스템 비저빌리티에 대한 빈 클래스입니다.
UINTERFACE 매크로를 사용합니다.
UInterface에서 상속합니다.
*/
UINTERFACE(MinimalAPI, Blueprintable)
class UReactToTriggerInterface : public UInterface
{
GENERATED_BODY()
};
/* 실제 인터페이스 선언. */
class IReactToTriggerInterface
{
GENERATED_BODY()
// 이 클래스에 인터페이스 함수를 추가합니다. 이 클래스는 이 인터페이스에서 구현하도록 상속될 클래스입니다.
public:
// 여기에 인터페이스 함수 선언을 추가합니다
};
이 예시에서 볼 수 있듯 실제 인터페이스에는 빈 클래스와 동일한 이름이 있지만 U 접두사는 I 로 대체됩니다. U 접두사 클래스는 생성자 및 다른 함수가 필요하지 않습니다. I 접두사 클래스는 인터페이스를 구현하려는 클래스로부터 상속되는 클래스이며 모든 인터페이스 함수를 포함합니다.
블루프린트가 이 인터페이스를 구현하려면 Blueprintable 지정자가 필요합니다.
인터페이스 지정자
인터페이스 지정자를 사용하여 클래스를 언리얼 리플렉션 시스템에 노출시킵니다. 다음 표에는 관련 인터페이스 지정자가 포함되어 있습니다.
| 인터페이스 지정자 | 설명 |
|---|---|
Blueprintable |
블루프린트에서 구현할 수 있도록 이 인터페이스를 노출시킵니다. BlueprintImplementableEvent 와 BlueprintNativeEvent 함수를 제외한 다른 것이 포함된 경우 인터페이스를 블루프린트에 노출시킬 수 없습니다. NotBlueprintable 또는 meta=(CannotImplementInterfaceInBlueprint) 를 사용하여 인터페이스가 블루프린트에서 구현하기에 안전하지 않음을 명시하세요. |
BlueprintType |
이 클래스를 블루프린트의 변수에 사용할 수 있는 타입으로 노출합니다. |
DependsOn=(ClassName1, ClassName2, ...) |
빌드 시스템에서 이 클래스를 컴파일하기 전에 이 지정자로 나열된 모든 클래스를 컴파일합니다. ClassName 은 동일한(또는 이전) 패키지의 클래스를 지정해야 합니다. 쉼표로 구분되는 단일 DependsOn 줄을 사용하여 여러 개의 종속성 클래스를 지정하거나 각 클래스에 대해 별도의 DependsOn 줄을 사용하여 지정할 수 있습니다. |
MinimalAPI |
다른 모듈에서 사용하도록 클래스의 타입 정보만 익스포트합니다. 클래스는 캐스팅될 수 있지만 인라인 메서드를 제외하면 클래스의 함수는 호출될 수 없습니다. 이는 다른 모듈에서 액세스할 수 있는 모든 함수가 필요하지 않은 클래스에 대해 모든 것을 익스포트할 필요가 없어 컴파일 시간이 단축됩니다. |
C++에서 인터페이스 구현하기
새 클래스에서 인터페이스를 사용하려면 다음을 수행합니다.
- 인터페이스 헤더 파일을 포함합니다.
I접두사 인터페이스 클래스에서 상속합니다.
다음은 이 페이지의 소개에서 언급된 함정의 예시입니다.
Trap.h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ReactToTriggerInterface.h"
#include "Trap.generated.h"
UCLASS(Blueprintable, Category="MyGame")
class ATrap : public AActor, public IReactToTriggerInterface
{
GENERATED_BODY()
public:
// 여기에 인터페이스 함수 오버라이드를 추가합니다
};
인터페이스 함수 선언하기
인터페이스에서 함수 선언에 사용할 수 있는 몇 가지 메서드가 있으며, 각각은 서로 다른 컨텍스트에서 구현 또는 호출이 가능합니다. 모두 인터페이스의 I 접두사 클래스에서 선언되어야 하고, 외부 클래스에서 표시되도록 퍼블릭으로 설정되어야 합니다.
C++ 전용 인터페이스 함수
UFUNCTION 지정자가 없는 인터페이스의 헤더 파일에서 가상 C++ 함수를 선언할 수 있습니다. 이러한 함수는 인터페이스를 구현하는 클래스에서 오버라이드할 수 있도록 가상 상태여야 합니다.
인터페이스 클래스
다음은 ReactToTriggerInterface 클래스에서의 인터페이스 클래스 예시입니다.
ReactToTriggerInterface.h
#pragma once
#include "ReactToTriggerInterface.generated.h"
/*
리플렉션 시스템 비저빌리티에 대한 빈 클래스입니다.
UINTERFACE 매크로를 사용합니다.
UInterface에서 상속합니다.
*/
UINTERFACE(MinimalAPI, Blueprintable)
class UReactToTriggerInterface : public UInterface
{
GENERATED_BODY()
};
/* 실제 인터페이스 선언. */
class IReactToTriggerInterface
{
GENERATED_BODY()
public:
virtual bool ReactToTrigger();
};
헤더 자체 내에 또는 인터페이스의 .cpp 파일 내에 디폴트 구현을 제공할 수 있습니다.
ReactToTriggerInterface.cpp
#include "ReactToTriggerInterface.h"
bool IReactToTriggerInterface::ReactToTrigger()
{
return false;
}
파생된 클래스
파생된 클래스에서 인터페이스를 구현할 때 해당 클래스에 오버라이드를 생성 및 구현할 수 있습니다. 다음은 ATrap 액터가 IReactToTriggerInterface 를 구현하는 경우의 모습에 대한 예시입니다.
Trap.h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ReactToTriggerInterface.h"
#include "Trap.generated.h"
UCLASS(Blueprintable, Category="MyGame")
class ATrap : public AActor, public IReactToTriggerInterface
{
GENERATED_BODY()
public:
virtual bool ReactToTrigger() override;
};
Trap.cpp
#include "Trap.h"
bool ATrap::ReactToTrigger()
{
return false;
}
이 방식으로 선언된 C++ 인터페이스 함수는 블루프린트에 표시되지 않고 블루프린트 인터페이스에 사용할 수 없습니다.
블루프린트 호출 가능 인터페이스 함수
블루프린트 호출 가능 인터페이스 함수를 만들려면 다음 작업을 수행해야 합니다.
BlueprintCallable지정자를 사용하여 함수의 선언에서UFUNCTION매크로를 지정합니다.BlueprintImplementableEvent또는BlueprintNativeEvent지정자를 사용합니다.
블루프린트 호출 가능 인터페이스 함수는 가상이 될 수 없습니다.
BlueprintCallable 지정자가 포함된 함수는 C++에서 또는 인터페이스를 구현하는 오브젝트에 대한 참조를 사용하는 블루프린트에서 호출될 수 있습니다.
블루프린트 호출 가능 함수가 값을 반환하지 않는 경우 언리얼 엔진은 함수를 블루프린트의 이벤트로 취급합니다.
블루프린트 구현 가능 이벤트
BlueprintImplementableEvent 지정자가 있는 함수는 C++에서 오버라이드할 수 없지만 인터페이스를 구현 또는 상속하는 블루프린트 클래스에서 오버라이드할 수 있습니다. 다음은 BlueprintImplementableEvent 에 대한 C++ 인터페이스 선언의 예시입니다.
ReactToTriggerInterface.h
#pragma once
#include "ReactToTriggerInterface.generated.h"
/*
리플렉션 시스템 비저빌리티에 대한 빈 클래스입니다.
UINTERFACE 매크로를 사용합니다.
UInterface에서 상속합니다.
*/
UINTERFACE(MinimalAPI, Blueprintable)
class UReactToTriggerInterface : public UInterface
{
GENERATED_BODY()
};
/* 실제 인터페이스 선언. */
class IReactToTriggerInterface
{
GENERATED_BODY()
public:
/* 블루프린트에서만 구현될 수 있는 React To Trigger 함수의 버전입니다. */
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category=Trigger Reaction)
bool ReactToTrigger();
};
블루프린트 네이티브 이벤트
BlueprintNativeEvent 지정자가 있는 함수는 C++ 또는 블루프린트에서 구현할 수 있습니다. 다음은 BlueprintNativeEvent 에 대한 C++ 인터페이스 선언의 예시입니다.
ReactToTriggerInterface.h
#pragma once
#include "ReactToTriggerInterface.generated.h"
/*
리플렉션 시스템 비저빌리티에 대한 빈 클래스입니다.
UINTERFACE 매크로를 사용합니다.
UInterface에서 상속합니다.
*/
UINTERFACE(MinimalAPI, Blueprintable)
class UReactToTriggerInterface : public UInterface
{
GENERATED_BODY()
};
/* 실제 인터페이스 선언. */
class IReactToTriggerInterface
{
GENERATED_BODY()
public:
/* C++ 또는 블루프린트에서 구현될 수 있는 React To Trigger 함수의 버전입니다. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category=Trigger Reaction)
bool ReactToTrigger();
};
C+에서 블루프린트 네이티브 이벤트 오버라이드
C++에서 BlueprintNativeEvent 를 구현하기 위해 BlueprintNativeEvent 와 동일한 이름에 _Implementation 접미사가 추가된 이름으로 함수를 생성합니다. 다음은 ATrap 에서의 예시입니다.
Trap.h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ReactToTriggerInterface.h"
#include "Trap.generated.h"
UCLASS(Blueprintable, Category="MyGame")
class ATrap : public AActor, public IReactToTriggerInterface
{
GENERATED_BODY()
public:
virtual bool ReactToTrigger() override;
// 블루프린트 네이티브 이벤트 오버라이드
bool ReactToTrigger_Implementation() override;
};
Trap.cpp
#include "Trap.h"
bool ATrap::ReactToTrigger()
{
return false;
}
// 블루프린트 네이티브 이벤트 오버라이드 구현
bool ATrap::ReactToTrigger_Implementation()
{
return false;
}
블루프린트에서 블루프린트 네이티브 이벤트 오버라이드
BlueprintNativeEvent 지정자는 또한 블루프린트에서의 구현 오버라이드를 허용합니다. 블루프린트에서 BlueprintNativeEvent 를 구현하려면 블루프린트 인터페이스 구현 문서를 참조하세요.
C++에서 블루프린트 이벤트 호출하기
C++의 Blueprintable 인터페이스에서 BlueprintImplementableEvent 또는 BlueprintNativeEvent 를 안전하게 호출하기 위해 특수 스태틱 Execute_ 함수 래퍼를 사용해야 합니다. 다음 예시 호출은 C++ 또는 블루프린트에서 구현되었는지 여부와 무관하게 작동합니다.
// OriginalObject는 IReactToTriggerInterface를 구현하는 오브젝트입니다
bool bReacted = IReactToTriggerInterface::Execute_ReactToTrigger(OriginalObject);
인터페이스 함수 타입
인터페이스 함수에는 3가지 타입이 있습니다.
- 베이스
- 구현
- 실행
다음 표에서는 각 타입의 용도를 설명합니다.
| 타입 | 정의 위치 | 목적 | 용도… |
|---|---|---|---|
| 베이스 함수 | 베이스 인터페이스 클래스. (MyInterface.h ) |
자손 클래스에서 구현할 수 있는 함수 정의. | 인터페이스와 구현이 C++에서만 정의된 경우 사용합니다. |
| 구현 래퍼 | 인터페이스를 구현하는 C++ 클래스. (MyInterfaceActor.h , MyInterfaceActor.cpp ) |
C++에서 인터페이스 함수 기능을 구현합니다. | 블루프린트 오버라이드가 아닌 C++ 구현만 호출합니다. |
| 실행 래퍼 | 언리얼 엔진의 리플렉션 시스템에서 자동 생성합니다. (MyInterface.generated.h , MyInterface.gen.cpp ) |
C++와 블루프린트에서 정의된 구현 간의 커뮤니케이션을 지원합니다. | C++ 및 블루프린트 오버라이드를 포함한 함수 구현을 호출합니다. |
예시는 다음과 같습니다.
MyFunction은MyInterface.h에서 정의된BlueprintNativeEvent인터페이스 함수입니다.MyInterfaceActor는MyInterface를 구현합니다.MyFunction_Implementation은MyInterfaceActor.cpp에서 정의됩니다.- 다양한 C++ 및 블루프린트가
MyInterfaceActor에서 상속한 액터를 스폰했습니다.
MyInterfaceActor 에서 상속하는 모든 블루프린트 및 C++ 오브젝트에서 MyFunction 을 안전하게 호출하려면 다음을 수행할 수 있습니다.
TArray<AActor*> OutActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AMyInterfaceActor::StaticClass(), OutActors);
// OutActors에는 AMyInterfaceActor에서 상속한 모든 BP 및 C++ 액터를 포함합니다
for (AActor* CurrentActor : OutActors)
{
// 각 CurrentActor는 자체 MyFunction 구현을 호출합니다
UE_LOG(LogTemp, Log, TEXT("%s : %s"), *CurrentActor->GetName(), *IMyInterface::Execute_MyFunction(Cast<AMyInterfaceActor>(CurrentActor)));
}
클래스의 인터페이스 구현 여부 확인
인터페이스를 구현하는 C++ 및 블루프린트 클래스 모두에 대한 호환성을 위해 다음 함수를 사용하여 클래스가 인터페이스를 구현하는지 여부를 확인합니다.
bool bIsImplemented;
/* OriginalObject가 UReactToTriggerInterface를 구현하는 경우 bIsImplemented가 true */
bIsImplemented = OriginalObject->GetClass()->ImplementsInterface(UReactToTriggerInterface::StaticClass());
/* OriginalObject가 UReactToTriggerInterface를 구현하는 경우 bIsImplemented가 true */
bIsImplemented = OriginalObject->Implements<UReactToTriggerInterface>();
/* OriginalObject가 C++에서 UReactToTriggerInterface를 구현하는 경우 ReactingObject는 null이 아님 */
IReactToTriggerInterface* ReactingObject = Cast<IReactToTriggerInterface>(OriginalObject);
템플릿 지정된 Cast<> 메서드는 인터페이스가 C++ 클래스에서 구현된 경우에만 작동합니다. 블루프린트에서 구현된 인터페이스는 오브젝트의 C++ 버전에서 존재하지 않으므로 Cast<> 는 null을 반환합니다. TScriptInterface<> 를 C++ 코드에서 사용하여 인터페이스 포인터와 이를 구현하는 UObject 를 안전하게 복사할 수도 있습니다.
다른 언리얼 타입으로 캐스팅
언리얼 엔진의 캐스팅 시스템은 한 인터페이스에서 다른 인터페이스로, 또는 한 인터페이스에서 적합한 언리얼 타입으로의 캐스팅을 지원합니다. 다음 예시는 인터페이스 캐스팅에 사용할 수 있는 몇 가지 방법입니다.
/* 인터페이스가 구현된 경우 ReactingObject는 null이 아님 */
IReactToTriggerInterface* ReactingObject = Cast<IReactToTriggerInterface>(OriginalObject);
/* ReactingObject가 null이 아니고 ISomeOtherInterface를 구현하는 경우 DifferentInterface는 null이 아님 */
ISomeOtherInterface* DifferentInterface = Cast<ISomeOtherInterface>(ReactingObject);
/* ReactingObject가 null이 아니고 OriginalObject가 AActor 또는 AActor 파생 클래스인 경우 ReactingActor는 null이 아님 */
AActor* ReactingActor = Cast<AActor>(ReactingObject);
오브젝트와 인터페이스 포인터를 안전하게 저장하기
특정 인터페이스를 구현하는 오브젝트에 대한 참조를 저장하기 위해 TScriptInterface 를 사용할 수 있습니다. 인터페이스를 구현하는 오브젝트가 있는 경우 TScriptInterface 를 다음과 같이 초기화할 수 있습니다.
UMyObject* MyObjectPtr;
TScriptInterface<IMyInterface> MyScriptInterface;
if (MyObjectPtr->Implements<UMyInterface>())
{
MyScriptInterface = TScriptInterface<IMyInterface>(MyObjectPtr);
}
// MyScriptInterface는 MyObjectPtr 및 MyInterfacePtr에 대한 참조 보유
원본 오브젝트에 대한 포인터를 얻으려면 GetObject 사용:
UMyObject* MyRetrievedObjectPtr = MyScriptInterface.GetObject();
원본 오브젝트가 구현하는 인터페이스에 대한 포인터를 얻으려면 GetInterface 사용:
IMyInterface* MyRetrievedInterfacePtr = MyScriptInterface.GetInterface();
TScriptInterface 에 관한 추가 정보는 TScriptInterface 와 연결된 FScriptInterface API 페이지를 참조하세요.
블루프린트 구현 가능 인터페이스
블루프린트에서 이 인터페이스를 구현하려면 Blueprintable 메타데이터 지정자를 사용해야 합니다. (스태틱 함수를 제외한) 모든 인터페이스 함수는 BlueprintNativeEvent 또는 BlueprintImplementableEvent 여야 합니다. 블루프린트가 C++에서 선언된 인터페이스를 구현할 때 블루프린트 인터페이스 에셋과 같이 작동합니다. 따라서 이 블루프린트 클래스의 인스턴스에는 인터페이스의 C++ 버전이 포함되지 않아 Cast<> 에서 사용할 수 없습니다. C++에서 Execute_ 스태틱 래퍼 함수만 제대로 작동할 것입니다.