클래스가 언리얼 인터페이스(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_
스태틱 래퍼 함수만 제대로 작동할 것입니다.