久しぶりの更新。
Blueprint のイベントディスパッチャは、Unreal C++ でいうところの Dynamic Multicast デリゲートらしい。
だが、Unreal C++ で用意する Blueprint 用のユーティリティには、Multicast は大げさすぎる局面が比較的多くある。
今回は、Multicast じゃない方のデリゲートを Blueprint からバインドする方法を見出したので、そのメモを残しておく。
自分のための復習:Unreal C++ のデリゲートに Blueprint のイベントをバインドする
Unreal C++ で用意したデリゲートを Blueprint でバインドできるようにするには、 DECLARE_DYNAMIC_MULTICAST_DELEGATE
マクロでデリゲート型を宣言し、UPROPERTY(BlueprintAssignable)
を指定したデリゲート変数を用意してやる。
以下は Actor Component から派生した Dynamic Delegate Component で、デリゲートの動作確認をする例。
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "DynamicDelegateComponent.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FBlueprintAssignableSignature);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class STATEMACHINE01_API UDynamicDelegateComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UDynamicDelegateComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
public: // [RINDERON] properties exposed to blueprint
UPROPERTY(BlueprintAssignable, Category = "Dynamic Delegate Component")
FBlueprintAssignableSignature OnEventDispatcherCalled;
};
// Called every frame
void UDynamicDelegateComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// [RINDERON]
OnEventDispatcherCalled.Broadcast();
}
この後、Blueprint 側で、アクタに Dynamic Delegate Component を追加して、
例えば Event BeginPlay
で以下のように組む。
これを実行すると、UDynamicDelegateComponent::TickComponent()
内のデリゲートの Broadcast()
が動いて、毎フレーム Print String が呼び出される。
Singlecast で十分な場合
- そもそもイベントは1個しかバインドする気がない場合
- むしろ1個、もしくは0個のイベントしかバインドできないようにしたい場合
- (知らない間にうっかり複数個バインドされてしまうと困る場合)
こういう局面は比較的多くあって、今回は Unreal C++ で用意した Singlecast なデリゲートを Blueprint でバインドできないか、いろいろ試してみた。
正直、できるとは思っていなかったのだが、できてしまった。
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "DynamicDelegateComponent.generated.h"
DECLARE_DYNAMIC_DELEGATE(FBlueprintCanBindThisSignature);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class STATEMACHINE01_API UDynamicDelegateComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UDynamicDelegateComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
public: // [RINDERON] properties exposed to blueprint
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dynamic Delegate Component")
FBlueprintCanBindThisSignature OnDelegateCalled;
};
// Called every frame
void UDynamicDelegateComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// [RINDERON]
OnDelegateCalled.ExecuteIfBound();
}
ポイントは2つ:
- Singlecast デリゲートを使いたいので、
DECLARE_DYNAMIC_MULTICAST_DELEGATE
ではなく、DECLARE_DYNAMIC_DELEGATE
で宣言をする。 - デリゲート変数を
UPROPERTY(BlueprintAssignable)
ではなく、UPROPERTY(BlueprintReadWrite)
など、普通に Blueprint に公開するプロパティのように扱ってやる。
Blueprint でのバインドの仕方は以下の通り。
変数にイベント(関数)をくっつける絵面が気持ち悪いような気もするが、そもそもデリゲートとはそういうものである。
実行結果は意図通り:
ご注意
- Dynamic デリゲートなので、関数名で検索されてから実行されるので、一定の負荷はかかる。
- Dynamic(動的)デリゲート - UE4公式 参照。
- どちらにせよ Dynamic なので、関数を一個呼び出すだけなら、Dynamic Multicast 版と同等程度の負荷だろう。負荷軽減の目的で使っても効果はないと思われる。
- メリットは、デリゲート変数を Blueprint に公開できることで、何か便利に使えそうということであり、負荷軽減ではない。
- ご利用は自己責任でお願いします。
- デリゲートを素で Blueprint に公開すること自体、Blueprint のコンセプトから外れているような気もします。(バレたら封印されるかも? …気にしすぎかしら?)