【UE5】C++でデリゲート【シングルキャスト、マルチキャスト】

当サイトではアフィリエイト広告を利用しています。
スポンサーリンク
未分類
  1. デリゲートとは
  2. デリゲートの流れ
    1. デリゲートが活躍する典型的なケース
    2. 例:宝箱を開けたことを他のクラスに通知する
      1. ① 宝箱クラスでイベントを定義(=宣言)
      2. ② ゲーム開始時に他クラスが反応関数をバインド(=BeginPlayなど)
      3. ③ プレイヤーなどが宝箱を開けたときにイベントを発火(=呼び出し)
    3. ✅ この例でわかること
    4. この設計が持つ強み
  3. デリゲートの種類
  4. 1. DECLARE_DYNAMIC_MULTICAST_DELEGATE
    1. 🔹 一番よく使われる通知イベント。Blueprintと連携しやすく、複数の関数を呼び出せるのが特徴。
      1. ✅ 宣言例(MyActor.h)
      2. ✅ バインド例(他のクラスなど)
      3. ✅ 発火例(イベントが起きたとき)
  5. 2. DECLARE_MULTICAST_DELEGATE
    1. 🔹 Blueprintなしで複数のC++関数を同時に呼び出したいときに便利。軽量&高速。
      1. ✅ 宣言例
      2. ✅ バインド例
      3. ✅ 発火例
  6. 3. DECLARE_DYNAMIC_DELEGATE
    1. 🔹 Blueprintから呼び出したいけど、1つの関数だけを呼ぶ用途に向いている。
      1. ✅ 宣言例
      2. ✅ バインド例
      3. ✅ 発火例
  7. 4. DECLARE_DELEGATE_OneParam
    1. 🔹 パラメータを渡したいときに使うC++専用の高速デリゲート。
      1. ✅ 宣言例
      2. ✅ バインド例
      3. ✅ 発火例
  8. 5. DECLARE_DELEGATE
    1. 🔹 パラメータなし・単一関数向け。最も軽量なC++専用デリゲート。
      1. ✅ 宣言例
      2. ✅ バインド例
      3. ✅ 発火例
  9. デリゲートの際の注意点
    1. ⚠️ デリゲート使用時の注意点とありがちなミス
    2. ❌ よくあるミス①:ゲッター経由でコピー → バインドが外れる!
      1. 🧠 どういうこと?
      2. ✅ 正しい使い方(参照で扱う or 直接呼ぶ)
    3. ❌ よくあるミス②:BindDynamic忘れ/AddDynamicとBindDynamicの混同
    4. ❌ よくあるミス③:マルチキャストに 1Execute() しようとする
    5. ✅ 注意点まとめ
    6. コンストラクタやBeginPlay以外でのバインドに注意

デリゲートとは

Unreal Engine 5(UE5)でのゲーム開発において、「デリゲート(Delegate)」は非常に重要な仕組みの一つです。簡単に言うと、「特定の処理が起きたときに、別の関数を呼び出す仕組み」のこと。C++でイベント駆動型の処理を書くには、デリゲートの活用が欠かせません。

デリゲートの流れ


デリゲートが活躍する典型的なケース

ゲーム開発では「何かが起きたときに、別のオブジェクトが反応する」という設計が頻繁に登場します。
たとえば──

  • プレイヤーが宝箱を開いたら、報酬を付与したい
  • ボスのHPが0になったら、エンディング演出を始めたい
  • ドアを開けたら、他のドアも開ける or サウンドを鳴らす

こうした 「通知」と「反応」の分離に、デリゲートは最適です。


例:宝箱を開けたことを他のクラスに通知する

① 宝箱クラスでイベントを定義(=宣言)

// MyChest.h

// 引数なしのデリゲートを定義
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnChestOpened);

UCLASS()
class AMyChest : public AActor
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintAssignable)
    FOnChestOpened OnChestOpened;

    void Open()
    {
        UE_LOG(LogTemp, Log, TEXT("宝箱が開かれた!"));

        // 通知を発信
        OnChestOpened.Broadcast();
    }
};

② ゲーム開始時に他クラスが反応関数をバインド(=BeginPlayなど)

// MyGameManager.cpp

void AMyGameManager::BeginPlay()
{
    Super::BeginPlay();

    if (TargetChest)
    {
        TargetChest->OnChestOpened.AddDynamic(this, &AMyGameManager::OnChestOpenedHandler);
    }
}

void AMyGameManager::OnChestOpenedHandler()
{
    UE_LOG(LogTemp, Log, TEXT("宝箱が開いたので報酬を与えます!"));
}

③ プレイヤーなどが宝箱を開けたときにイベントを発火(=呼び出し)

// プレイヤーの操作などから宝箱を開く
ChestActor->Open();

✅ この例でわかること

処理説明
宣言イベントを発信できるようにする(宝箱に通知機能を持たせる)
バインド別のクラス(マネージャーなど)がそれに反応するように登録
発火宝箱が開かれたタイミングでイベントを発信(Broadcast)

この設計が持つ強み

  • 疎結合:宝箱は「誰が聞いてるか」を気にせず開くことだけに集中
  • 再利用性:同じ通知に複数のクラスが反応できる(SE、UI、報酬など)
  • 柔軟性:反応側を差し替えたり、増やしたりが簡単

この中では疎結合を保ちやすいことが特に重要です。全く別のカテゴリのソースに依存関係がつくとかなり面倒なことになります。今回はあまり触れませんが


このような 典型的な通知パターンを出発点として、次は各デリゲートの種類と特徴を理解していきましょう!

デリゲートの種類

デリゲートの種類用途特徴
DECLARE_DELEGATE通常のC++用イベント通知パラメータ無し / 高速
DECLARE_DELEGATE_OneParamパラメータ1個あり型安全
DECLARE_DYNAMIC_DELEGATEBlueprintでも使いたいとき反射に対応(遅いが便利)
DECLARE_MULTICAST_DELEGATE複数の関数を同時に呼びたいObserver的使い方
DECLARE_DYNAMIC_MULTICAST_DELEGATEBlueprint&複数対応最も一般的な通知イベント(例: OnClickedなど)

1. DECLARE_DYNAMIC_MULTICAST_DELEGATE

🔹 一番よく使われる通知イベント。Blueprintと連携しやすく、複数の関数を呼び出せるのが特徴。

✅ 宣言例(MyActor.h)

// 引数なしの通知イベントを定義
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnOpened);

UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()
public:
    // 外部からバインド可能に
    UPROPERTY(BlueprintAssignable)
    FOnOpened OnOpened;
};

✅ バインド例(他のクラスなど)

// MyActorInstance の OnOpened イベントに自分の関数を登録
MyActorInstance->OnOpened.AddDynamic(this, &UMyComponent::HandleOpened);

✅ 発火例(イベントが起きたとき)

// OnOpenedに登録された全関数を呼び出す
OnOpened.Broadcast();

2. DECLARE_MULTICAST_DELEGATE

🔹 Blueprintなしで複数のC++関数を同時に呼び出したいときに便利。軽量&高速。

✅ 宣言例

DECLARE_MULTICAST_DELEGATE(FOnHealthZero);

class FMyHealthSystem
{
public:
    FOnHealthZero OnHealthZero;
};

✅ バインド例

HealthSystem.OnHealthZero.AddRaw(this, &MyClass::HandleDeath);

✅ 発火例

OnHealthZero.Broadcast();

3. DECLARE_DYNAMIC_DELEGATE

🔹 Blueprintから呼び出したいけど、1つの関数だけを呼ぶ用途に向いている。

✅ 宣言例

DECLARE_DYNAMIC_DELEGATE(FOnFinishFade);

FOnFinishFade OnFadeComplete;

✅ バインド例

OnFadeComplete.BindDynamic(this, &UMyWidget::OnFadeFinished);

✅ 発火例

if (OnFadeComplete.IsBound())
{
    OnFadeComplete.Execute();
}

4. DECLARE_DELEGATE_OneParam

🔹 パラメータを渡したいときに使うC++専用の高速デリゲート。

✅ 宣言例

DECLARE_DELEGATE_OneParam(FOnDamageTaken, float);

FOnDamageTaken OnDamageTaken;

✅ バインド例

OnDamageTaken.BindRaw(this, &MyClass::HandleDamage);

✅ 発火例

OnDamageTaken.ExecuteIfBound(25.0f);

5. DECLARE_DELEGATE

🔹 パラメータなし・単一関数向け。最も軽量なC++専用デリゲート。

✅ 宣言例

DECLARE_DELEGATE(FOnJump);

FOnJump OnJump;

✅ バインド例

OnJump.BindRaw(this, &MyClass::DoJump);

✅ 発火例

OnJump.ExecuteIfBound();

デリゲートの際の注意点

了解です!
以下は **「UE5のデリゲート使用時に気をつけたいポイントとよくあるミス」をWordPressのブログ記事向けに、H3見出しから始まるGutenbergブロック構成でまとめた内容です。
特に
「安易なゲッターによるバインド外れ」**を重点的に解説しています。


⚠️ デリゲート使用時の注意点とありがちなミス

UE5のデリゲートはとても便利ですが、使い方を誤ると思わぬバグを招くことがあります。
ここでは、デリゲート使用時に気をつけたいポイントと、ありがちな落とし穴を紹介します。


❌ よくあるミス①:ゲッター経由でコピー → バインドが外れる!

デリゲートを返す**ゲッター関数(GetXXXDelegate)**を用意している場合に、下記のような書き方をすると、思ったように関数が呼ばれないことがあります。

// よくあるNGコード
FDynamicBoolDelegate LocalCopy = OtherClass->GetDelegate(); // ← 値渡しでコピー!
LocalCopy.ExecuteIfBound(); // ← 実はバインドされてない!

🧠 どういうこと?

1
FDynamicBoolDelegate
コピー時にバインド情報を引き継ぎません!
つまり、
1
GetDelegate()
で取得した値は「実体ではなく、バインド情報のないコピー」になってしまうのです。

✅ 正しい使い方(参照で扱う or 直接呼ぶ)

// 参照で取得
FDynamicBoolDelegate& Ref = OtherClass->GetDelegate();
Ref.ExecuteIfBound();

// もしくはそのまま実体に対して呼ぶ
OtherClass->GetDelegate().ExecuteIfBound();

❌ よくあるミス②:BindDynamic忘れ/AddDynamicとBindDynamicの混同

1
DECLARE_DYNAMIC_~
系のデリゲートは
1
BindDynamic
1
AddDynamic
を使ってバインドします。
しかし、
1
BindRaw
1
BindLambda
を使うとコンパイルエラーになります(内部で反射を使っているため非対応)。

// NG:Blueprint向けのデリゲートにRawバインド
MyDynamicDelegate.BindRaw(this, &MyClass::OnCalled); // ✕

// OK:
MyDynamicDelegate.BindDynamic(this, &UMyClass::OnCalled); // ◯

❌ よくあるミス③:マルチキャストに 1Execute() しようとする

1
DECLARE_DYNAMIC_MULTICAST_DELEGATE
で定義したデリゲートは、単一の関数を呼び出す Execute() や ExecuteIfBound() が使えません。
代わりに
1
Broadcast()
を使いましょう。

// NG:
MyMulticast.ExecuteIfBound(); // ✕

– OK:
MyMulticast.Broadcast(); // ◯

✅ 注意点まとめ

落とし穴対策・コメント
ゲッター経由のコピー必ず参照で受け取るか、直接呼ぶ
Blueprint向けデリゲートに Raw でバインド
1
BindDynamic
を使う
マルチキャストに Execute() 使用
1
Broadcast()
を使う
1
IsBound()
忘れ
1
ExecuteIfBound()
を使えばOK
一度しか使わない一時デリゲートを使いまわそうとするスコープ外になって無効化される可能性あり

コンストラクタやBeginPlay以外でのバインドに注意

Actorがまだ無効なタイミングや、Garbage Collectionの対象になる前後でのバインドは意図しない動作を招くことがあります。
可能なら、

1
BeginPlay()
1
OnRegister()
などで安全にバインド処理を行いましょう。

コメント

タイトルとURLをコピーしました