Interface クラスは関連性のない一連のクラスが共通の関数を確実に実装するために役立ちます。これはあるゲーム機能が、それ以外は類似性がない大きく複雑なクラスによって共有される場合に非常に便利です。例えば、ゲームではトリガー ボリュームに入るとトラップがアクティベートし、敵が警戒したり、プレイヤーにポイントを報酬として与えるなどのシステムがあります。これはトラップ、敵、ポイント付与の "ReactToTrigger" 関数によって実装されることがあります。ただし、トラップは AActor`から、敵は特殊な
APawn または
ACharacter`サブクラスから、ポイント付与は UDataAsset
から派生することがあります。こうしたクラスはすべて共有の機能を必要としますが、UObject
以外は共通の 祖先 (ancestor) がありません。この場合、インターフェースをお勧めします。
インターフェースの宣言
インターフェース クラスの宣言は通常の Unreal クラスの宣言と似ていますが、大きな違いが 2 つあります。まず、インターフェース クラスは UCLASS マクロの代わりに UINTERFACE マクロを使用します。また、直接 UObject
からではなく UInterface
から継承します。
UINTERFACE([specifier, specifier, ...], [meta(key=value, key=value, ...)])
class UClassName : public UInterface
{
GENERATED_BODY()
};
2 つめの違いは、UINTERFACE クラスは実際のインターフェースではありません。Unreal Engine のリフレクション システムで見えるようにするためにだけ存在する空のクラスです。他のクラスによって継承される実際のインターフェースは、必ず同じクラス名を持たなければなりませんが、イニシャルの "U" が "I" に変わっています。
ヘッダー ファイルで (例、 ReactToTriggerInterface.h
) 以下のようにします。
#pragma once
#include "ReactToTriggerInterface.generated.h"
UINTERFACE(MinimalAPI, Blueprintable)
class UReactToTriggerInterface : public UInterface
{
GENERATED_BODY()
};
class IReactToTriggerInterface
{
GENERATED_BODY()
public:
/** Add interface function declarations here */
};
"U プリフィックス" クラスはコンストラクタや他の関数を必要としません。"I プリフィックス" クラスはすべてのインターフェース関数を含み、他のクラスによって実際に継承されるものになります。
Blueprintable
インターフェース指定子
インターフェース指定子 |
意味 |
---|---|
|
ブループリントの変数に使用できるタイプとしてクラスを公開します。 |
|
リストされている全クラスは、このクラスより前にコンパイルされます。ClassName は同じ (あるいは以前の) パッケージのクラスを指定しなければなりません。1 行の |
|
他のモジュールで使用するために、クラスの型情報のみエクスポートさせます。クラスをキャストすることができますが、クラスの関数を呼び出すことはできません (インライン メソッドは除く)。そのため、他のモジュールからすべての関数にアクセス可能である必要のないクラスで何もかもをエクスポートしないことでコンパイル時間を短縮できます。 |
C++ でインターフェースを実装する
新規クラスでインターフェースを使用するには、"I プリフィックス" インターフェース クラスから継承します (使用している UObject
ベースのクラスに加えて)。
#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:
/** Add interface function overrides here. */
}
インターフェース関数を宣言する
インターフェースで関数を宣言する方法は何種類かありますが、実装や呼び出しが可能な状況はそれぞれ異なります。すべての関数は先頭に「I」が付いたクラスでインターフェースに対して宣言し、外部クラスからも見えるように public
にしなければなりません。
C++ インターフェースのみの関数
インターフェースのヘッダ ファイルで UFUNCTION
指定子を付けずに仮想 C++ 関数を宣言することができます。インターフェースを実装するクラスでオーバーライドできるように関数は仮想でなければなりません。
public:
virtual bool ReactToTrigger();
ヘッダ内、またはインターフェースの .cpp
ファイルのいずれかでデフォルト実装を指定できます。
bool IReactToTriggerInterface::ReactToTrigger()
{
return false;
}
Actor クラスでインターフェースを実装する場合、そのクラス固有のオーバーライドを作成して実装できます。
public:
virtual bool ReactToTrigger() override;
bool ATrap::ReactToTrigger()
{
return false;
}
ただし、これらの C++ インターフェース関数はブループリントでは表示されません。
ブループリントでの呼び出しが可能な関数
ブループリントでの呼び出しが可能な関数を作成するには、BlueprintCallable
指定子を使った関数の宣言で UFUNCTION
マクロを指定しなければなりません。BlueprintImplementableEvent
指定子、または BlueprintNativeEvent
指定子のいずれかを使用する必要があり、関数は仮想であってはなりません。
public:
/**A version of React To Trigger that can be implemented in Blueprint only. */
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category=Trigger Reaction)
bool ReactToTrigger();
public:
/**A version of React To Trigger that can be implemented in C++ or Blueprint. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category=Trigger Reaction)
bool ReactToTrigger();
BlueprintCallable
BlueprintCallable
指定子を使った関数は、インターフェースを実装しているオブジェクトへの参照を使って C++ またはブループリントで呼び出すことができます。
BlueprintImplementableEvent
BlueprintImplementableEvent
を使った関数は C++ でオーバーライドすることはできませんが、インターフェースを実装または継承しているブループリント クラスではオーバーライドが可能です。
BlueprintNativeEvent
BlueprintNativeEvent
を使った関数は同じ名前の関数をオーバーライドすることで C++ で実装することができますが、末尾に _Implementation
を追加します。
public:
bool ReactToTrigger_Implementation() override;
bool ATrap::ReactToTrigger_Implementation() const
{
return false;
}
この指定子は実装をブループリントでオーバーライドすることも可能です。
任意のクラスがインターフェースを実装するかを判断する
インターフェースを実装する C++ と Blueprint のクラスの両方で互換性を得るためには、以下のいずれかの関数を使用します。
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
の使用を試みるとすると失敗し、コードもコンパイルされません。
他の Unreal タイプにキャストする
Unreal Engine は、あるインターフェースから別のインターフェースへのキャスト、もしくはあるインターフェースからあるアンリアル タイプへのキャストを適宜サポートしています。
IReactToTriggerInterface* ReactingObject = Cast<IReactToTriggerInterface>(OriginalObject); // ReactingObject will be non-null if the interface is implemented. (インターフェースが実装されている場合は ReactingObject は null ではない)
ISomeOtherInterface* DifferentInterface = Cast<ISomeOtherInterface>(ReactingObject); // DifferentInterface will be non-null if ReactingObject is non-null and also implements ISomeOtherInterface.(ReactingObject が null でなく、かつ ISomeOtherInterface を実装する場合、DifferentInterface は null ではない)
AActor* Actor = Cast<AActor>(ReactingObject); // Actor will be non-null if ReactingObject is non-null and OriginalObject is an AActor or AActor-derived class.(ReactingObject is が null でなく、かつ OriginalObject が AActor もしくは AActor 派生クラスの場合、アクタは null ではない)
Blueprint Implementable クラス
ブループリントにこのインターフェースを実装させたい場合は、Blueprintable
のメタデータの指定子を使用しなければなりません。Blueprint クラスがオーバーライドしようとする関数は、 BlueprintNativeEvent
または BlueprintImplementableEvent
でなければなりません。BlueprintCallable
としてマークされる関数も呼び出し可能ですが、オーバーライドはされません。他のすべての関数はブループリントからアクセスできません。