10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UE4 インターフェイスについてのメモ

Last updated at Posted at 2020-11-29

概要

UnrealEngine のインターフェイスについてのメモ書きです。

環境

Windows10
Visual Studio 2017
UnrealEngine 4.25, 5.3

更新履歴

日付 内容
2020/11/09 初版
2024/08/07 インターフェイスの実装確認について追記
2024/10/21 インターフェイスの参照保持について追記

参考

以下を参考にさせて頂きました、ありがとうございます。

Unreal Engine : 公式
【Unreal C++】⑥Interface【UE4】
Interfaces In C++ | UE4 Community Wiki
【UE4】InterfaceとDispatcherの使い分け方-自分流

インターフェイスについて

インターフェイスは実装を縛る仕組みでクラスの依存度を下げるのに有用です。
C++にはインターフェイスがないため継承や純粋仮想クラスを使ったインターフェイスクラスで実現していますが、Unreal C++ も似たような感じのようです。

BPにてアクセスしたい相手BPクラスにキャストするケースがありますが、こういう場合にインターフェイスを使うことが望ましいようです。これはBPの参照が増えることによるビルド時間が延びてしまうことを抑えることができるためだと思われます。

Unreal C++ でのインターフェイス作成1

インターフェイス定義

UInterface を継承したクラスを宣言し、UプリフィックスをIに変えたクラスを宣言し、実装を縛るメソッドを宣言します。この時、GENERATED_***_BODY() が UI で違うことに注意。

またメタ情報の CannotImplementInterfaceInBlueprint はC++での実装に制限する指定です。

MyInterface.h
#pragma once

#include "CoreMinimal.h"
#include "MyIntarface.generated.h"

UINTERFACE(BlueprintType, meta = (CannotImplementInterfaceInBlueprint))
class MYPROJECT2_API UPerson : public UInterface
{
	GENERATED_UINTERFACE_BODY()
};

class MYPROJECT_API IPerson
{
	GENERATED_IINTERFACE_BODY()

public:
	// 実装したいメソッド
	virtual FString		Greeting();
};

Uプリフィックスのコンストラクタと、Iプリフィックスの実装メソッドデフォルト実装を書きます。この時点でインターフェイスぽくないです。。

MyInterface.cpp
#include "MyInterface.h"

UPerson::UPerson(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

FString IPerson::Greeting()
{
	return( FString(TEXT("....")) );
}

C++でインターフェイスを実装する

あとはインターフェイスクラスを継承してクラス宣言します。

MyClass.h

UCLASS()
class MYPROJECT_API UJapanese : public UObject, public IPerson
{
	GENERATED_BODY()
	
public:
	// オーバーライドする
	virtual FString		Greeting() override { return( FString::Printf(TEXT("こんにちは!")) ); }
};

デフォルト実装を持っているので、そちらを呼び出すこともできます。

MyClass.h
UCLASS()
class MYPROJECT_API UAmerican : public UObject, public IPerson
{
	GENERATED_BODY()
	
public:
	// 元の実装を呼び出す
	virtual FString		Greeting() override { return(IPerson::Greeting()); }
};

Unreal C++ でのインターフェイス作成2

インターフェイス定義

BlueprintImplementableEvent BlueprintNativeEvent をつけたインターフェイスを定義します。

MySampleInterface.h
#pragma once
#include "MySampleInterface.generated.h"

UINTERFACE(Blueprintable)
class UMySampleInterface : public UInterface
{
    GENERATED_BODY()
};

class IMySampleInterface
{
	GENERATED_BODY()

public:

	UFUNCTION(Category="Test", BlueprintImplementableEvent, BlueprintCallable)
	void Func0();
	UFUNCTION(Category="Test", BlueprintImplementableEvent, BlueprintCallable)
	void Func0Arg(int32 Value);
	UFUNCTION(Category="Test", BlueprintNativeEvent, BlueprintCallable)
	int32 Func1();
	UFUNCTION(Category="Test", BlueprintNativeEvent, BlueprintCallable)
	int32 Func1Arg(int32 Value);
};

GENERATED_BODY()マクロで作成した場合は、デフォルト実装のコンストラクタが不要で、GENERATED_UINTERFACE_BODY() マクロで作成した場合はデフォルト実装のコンストラクタが必要の様です。

実装インターフェイス

実装インターフェイス で実装してみます。
Implementation_Setting.jpg

関数は以下の様になります。
Implementation_Func.jpg

Func0Func0Arg はイベントで実装します。
Func0_BP.jpg

Func1Func1Arg は関数で実装します。
Func1_BP.jpg

Func1Arg_BP.jpg

呼び出しテスト

以下のコードを使ってインターフェイスの呼び出しを行います。

.cpp
#include "Kismet/GameplayStatics.h"

TArray<AActor*> _TargetActors;
// インターフェース取得
UGameplayStatics::GetAllActorsWithInterface(this->GetWorld(), UMySampleInterface::StaticClass(), _TargetActors);
// インターフェースを実行
for (auto _Actor : _TargetActors)
{
	IMySampleInterface::Execute_Func0(_Actor);
	IMySampleInterface::Execute_Func0Arg(_Actor, 100);
	IMySampleInterface::Execute_Func1(_Actor);
	IMySampleInterface::Execute_Func1Arg(_Actor, 200);
}

BPで呼び出す場合は以下の様になります。
execute_interfaceBP.jpg

以下の様になりました。問題なく呼び出せました。
Result.jpg

継承インターフェイス

継承インターフェイス で実装してみます。
Inheritance_Setting.jpg

関数は以下の様になります。
Inheritance_Func.jpg

実装インターフェイスの場合と同様に実装でき、同様の結果になります。

BPでインターフェイスを実装する

BPエディタで開いて
[クラス設定] -> [詳細] -> [インターフェイス] で追加できます。

BP_ClassSetting.jpg

CannotImplementInterfaceInBlueprint を付与したインターフェイスは実装インターフェイスでは追加できません。

継承インターフェイス はBPが継承したクラスが実装しているインターフェイスが表示されます。

BP でのインターフェイス作成

インターフェイス定義

コンテンツフォルダから右クリックで「ブループリントインターフェイス」で作成できます。
interfaceBP.jpg

BPインターフェイスにて関数名や引き数、返り値が設定できます。
interfaceBP_Setting.jpg

BPクラスでの実装

このBPインターフェイスを実装したいBPクラスに対し実装インターフェイスで追加して使用します。[クラス設定] -> [詳細] -> [インターフェイス]

追加するとインターフェイスイベントを実装できるようになります。

interfaceBP_Event.jpg

インターフェイス実装確認

インターフェイスの実装を確認するには Implementsを使うか、キャストして確認することができます。以下サンプルコード。

.cpp
// インターフェイス実装の確認(`I` ではなく `U` のほうで指定する)
if( _Object->Implements<UMySampleInterface>() ){
	// 実装されている
}

// キャストしてインターフェイスの実装を確認する
auto _CastObject = Cast<IMySampleInterface>(_Object);
if( _CastObject){
	// 実装されている
}

インターフェイスの参照保持

UPROPERTY() をつけてインターフェイスの参照を保持するには TScriptInterfaceを使う必要があります。以下サンプルコード。

.h
	UPROPERTY()
	TScriptInterface<IMyInterface>	MyInterface;

オブジェクトとインターフェイスを両方設定します。

.cpp
// MyObject からインターフェイス参照を保持する
MyInterface.SetObject(MyObject);
MyInterface.SetInterface(Cast<IMyInterface>(MyObject));

// インターフェイスに定義されている SampleProc() メソッドを使う
MyInterface.GetInterface()->SampleProc();

まとめ

UEのインターフェイスはイベントメッセージ的な面が強い気がします。
クラスの依存度を下げるためにも積極的に使っていきたいところですが、定義の記述方法はもう少しどうにかなりませんかね。。

10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?