【UE4】C++の (Singlecast) Dynamic Delegate に Blueprint からバインドする


久しぶりの更新。

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 で、デリゲートの動作確認をする例。

(例)DynamicDelegateComponent.h
#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;
};
(例)DynamicDelegateComponent.cpp(一部)
// 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 でバインドできないか、いろいろ試してみた。
正直、できるとは思っていなかったのだが、できてしまった。

(例)DynamicDelegateComponent.h(改)
#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;
};
(例)DynamicDelegateComponent.cpp(改/一部)
// 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 のコンセプトから外れているような気もします。(バレたら封印されるかも? …気にしすぎかしら?)