인터페이스 클래스는 잠재적으로 관련되지 않은 클래스 세트가 공통 함수 세트를 구현하도록 하는 데 사용됩니다. 이는 일부 게임 함수가 다른 크고 복잡한 클래스에서 공유될 수 있는 경우에 유용합니다.
게임에는 트리거 볼륨에 들어가는 플레이어 캐릭터가 함정을 활성화하거나 적의 주의를 끌거나 포인트를 획득하는 시스템이 있을 수 있습니다. 이는 함정, 적 또는 포인트 획득에 대한 'ReactToTrigger' 함수로 구현할 수 있습니다. 그러나 함정은 Aactor에서, 적은 특수화된 APawn 또는 ACharacter 서브클래스에서, 그리고 포인트 획득은 UDataAsset 에서 파생될 수 있습니다.
이 클래스들은 모두 공유 함수 기능이 필요하지만 UObject 외에는 공통 부모가 없습니다. 이러한 경우 인터페이스가 권장됩니다.
인터페이스 선언
인터페이스 클래스 선언은 평범한 언리얼 클래스 선언과 유사하지만 두 가지 주요 차이점이 있습니다. 첫째, 인터페이스 클래스는 UCLASS 매크로 대신 UINTERFACE 매크로를 사용하고 UObject 대신 Uinterface 를 직접 상속합니다.
UINTERFACE([specifier, specifier, ...], [meta(key=value, key=value, ...)])
class UClassName : public UInterface
{
GENERATED_BODY()
};
둘째, UINTERFACE 클래스는 실제 인터페이스가 아닙니다. 언리얼 엔진의 리플렉션 시스템에 대한 비저빌리티를 위해서만 존재하는 빈 클래스입니다. 다른 클래스에서 상속되는 실제 인터페이스는 클래스 이름이 동일해야 하지만 첫 'U'가 'I'로 변경됩니다.
.h 파일(예: ReactToTriggerInterface.h)을 다음과 같이 수정합니다.
#pragma once
#include "ReactToTriggerInterface.generated.h"
UINTERFACE(MinimalAPI, Blueprintable)
class UReactToTriggerInterface : public UInterface
{
GENERATED_BODY()
};
class IReactToTriggerInterface
{
GENERATED_BODY()
public:
/** 여기에 인터페이스 함수 선언을 추가하세요 */
};
'접두사 U가 붙은' 클래스에는 생성자나 다른 함수가 필요하지 않지만 '접두사 I가 붙은' 클래스는 모든 인터페이스 함수를 포함하며 실제로 다른 클래스에서 상속됩니다.
블루프린트가 이 인터페이스를 구현하도록 하려면 Blueprintable 지정자가 필요합니다.
인터페이스 지정자
인터페이스 지정자는 클래스를 언리얼 리플렉션 시스템에 노출하는 데 사용됩니다. 아래 표를 참조하세요.
| 인터페이스 지정자 | 의미 |
|---|---|
BlueprintType |
이 클래스를 블루프린트의 변수에 사용할 수 있는 타입으로 노출합니다. |
DependsOn=(ClassName1, ClassName2, ...) |
나열된 모든 클래스가 이 클래스보다 먼저 컴파일됩니다. ClassName은 동일한(또는 이전) 패키지의 클래스를 지정해야 합니다. 복수의 종속성 클래스는 쉼표로 구분된 단일 DependsOn 줄을 사용하여 지정하거나 각 클래스에 대해 별도의 DependsOn 줄을 사용하여 지정할 수 있습니다. 컴파일러는 이미 컴파일한 클래스에 무엇이 있는지만 알기 때문에 클래스가 다른 클래스에 선언된 구조체나 열거형을 사용할 때 중요합니다. |
MinimalAPI |
클래스의 타입 정보만 다른 모듈에서 사용할 수 있도록 익스포트합니다. 클래스는 형변환될 수 있지만 클래스의 함수는 호출될 수 없습니다(인라인 메서드 제외). 다른 모듈에서 액세스할 수 있는 모든 함수가 필요하지 않은 클래스에 대해 모든 것을 익스포트하지 않음으로써 컴파일 시간을 향상합니다. |
C++에서 인터페이스 구현하기
(사용 중인 UObject 기반 클래스 이외의) 새 클래스에서 인터페이스를 사용하려면 '접두사 I가 붙은' 인터페이스 클래스에서 상속하면 됩니다.
#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++ 함수를 선언할 수 있습니다. 이러한 함수는 인터페이스를 구현하는 클래스에서 오버라이드할 수 있도록 버추얼이어야 합니다.
public:
virtual bool ReactToTrigger();
그런 다음 헤더 자체 또는 인터페이스의 .cpp 파일 내에서 기본 구현을 제공할 수 있습니다.
bool IReactToTriggerInterface::ReactToTrigger()
{
return false;
}
액터 클래스에서 인터페이스를 구현할 때 해당 클래스에 특정한 오버라이드를 생성 및 구현할 수 있습니다.
Trap.h public:
virtual bool ReactToTrigger() override;
Trap.cpp
bool ATrap::ReactToTrigger()
{
return false;
}
그러나 이러한 C++ 인터페이스 함수는 블루프린트에 표시되지 않습니다.
블루프린트 호출 가능 인터페이스 함수
블루프린트 호출 가능 인터페이스 함수를 만들려면 BlueprintCallable 지정자와 함께 함수 선언에 UFUNCTION 매크로를 제공해야 합니다. 또한 BlueprintImplementableEvent 또는 BlueprintNativeEvent 지정자를 사용해야 하며 함수는 버추얼이 아니어야 합니다.
public:
/**블루프린트에서만 구현할 수 있는 트리거에 반응 버전입니다. */
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category=Trigger Reaction)
bool ReactToTrigger();
ReactToTrigger.h
public:
/**C++ 또는 블루프린트에서 구현할 수 있는 트리거에 반응 버전입니다. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category=Trigger Reaction)
bool ReactToTrigger();
BlueprintCallable
'BlueprintCallable' 지정자를 사용하는 함수는 인터페이스를 구현하는 오브젝트에 대한 레퍼런스를 사용하여 C++ 또는 블루프린트에서 호출할 수 있습니다.
BlueprintImplementableEvent
BlueprintImplementableEvent 를 사용하는 함수는 C++에서 오버라이드할 수 없지만 인터페이스를 구현하거나 상속하는 모든 블루프린트 클래스에서는 오버라이드할 수 있습니다.
BlueprintNativeEvent
BlueprintNativeEvent 를 사용하는 함수는 이름은 같지만 끝에 _Implementation 접미사가 붙은 함수를 오버라이드하여 C++에서 구현할 수 있습니다.
public:
bool ReactToTrigger_Implementation() override;
Trap.cpp
bool ATrap::ReactToTrigger_Implementation() const
{
return false;
}
이 지정자를 사용해도 블루프린트에서 구현을 오버라이드할 수 있습니다.
클래스가 인터페이스를 구현하는지 여부를 결정하기
인터페이스를 구현하는 C++ 및 블루프린트 클래스와의 호환성을 위해 다음 함수 중 하나를 사용하세요.
bool bIsImplemented = OriginalObject->GetClass()->ImplementsInterface(UReactToTriggerInterface::StaticClass()); // bIsImplemented will be true if OriginalObject implements UReactToTriggerInterface.
bIsImplemented = OriginalObject->Implements<UReactToTriggerInterface>(); // bIsImplemented will be true if OriginalObject implements UReactToTriggerInterfacce.
IReactToTriggerInterface* ReactingObjectA = Cast<IReactToTriggerInterface>(OriginalObject); // ReactingObject will be non-null if OriginalObject implements UReactToTriggerInterface.
StaticClass 함수가 '접두사 I가 붙은' 클래스에 구현되지 않은 경우 '접두사 U가 붙은' 클래스에서 Cast 를 사용하려고 시도하면 실패하고 코드가 컴파일되지 않습니다.
다른 언리얼 타입에 대한 형변환
언리얼 엔진의 형변환 시스템은 적절한 경우 한 인터페이스에서 다른 인터페이스로 또는 인터페이스에서 언리얼 타입으로의 형변환을 지원합니다.
IReactToTriggerInterface* ReactingObject = Cast<IReactToTriggerInterface>(OriginalObject); // ReactingObject will be non-null if the interface is implemented.
ISomeOtherInterface* DifferentInterface = Cast<ISomeOtherInterface>(ReactingObject); // DifferentInterface will be non-null if ReactingObject is non-null and also implements ISomeOtherInterface.
AActor* Actor = Cast<AActor>(ReactingObject); // Actor will be non-null if ReactingObject is non-null and OriginalObject is an AActor or AActor-derived class.
블루프린트 구현 가능한 클래스
블루프린트가 이 인터페이스를 구현할 수 있게 하려면 Blueprintable 메타데이터 지정자를 사용해야 합니다. 블루프린트 클래스가 오버라이드하려는 모든 인터페이스 함수는 BlueprintNativeEvent 또는 BlueprintImplementableEvent 여야 합니다. BlueprintCallable 로 표시된 함수는 계속 호출할 수 있지만 오버라이드할 수는 없습니다. 다른 모든 함수는 블루프린트에서 액세스할 수 없습니다.