遅延イベントキュー

イベントを今キューに入れ、後から選んだフラッシュポイントで順序どおりにパブリッシュする。

危険な場所ではイベントをキューに入れ、自分で選んだ安全なポイントでバッチ全体を順序どおりにパブリッシュします。

インターフェースIDeferredBus
オフスイッチNullDeferredBus
アセンブリCommonGameSystem.Core
起動起動時に自動登録(23 サービスの 1 つ)— セットアップ不要

概要

遅延イベントキューは、イベントバス(IEventBus)の上に載る薄い層です。イベントを即座にパブリッシュする代わりに Enqueue します。キューは Flush() を呼ぶまですべてを保持し、Flush() がバッチ全体をイベントバス経由でパブリッシュします — すべてのイベント型を横断して、キューに入れられた正確な順序で。

イベントのパブリッシュを危険な場所 — 物理コールバック、変更中でもあるコレクションへのループ、入力処理の途中 — から、自分で選んだ安全なポイントへ移すために使います。キューが所有するのは保持と順序だけです。ルーティングと購読者の処理はイベントバスに残ります。自動フラッシュはありません:バッチをいつ発火するかはあなたが決めます。メインスレッド専用です。

クイックスタート

イベントをプレーンなクラスとして定義します:

public sealed class EnemyDiedEvent
{
    public int EnemyId { get; }
    public EnemyDiedEvent(int enemyId) { EnemyId = enemyId; }
}

そして今キューに入れ、後でパブリッシュします:

using CommonGameSystem.Core;
using UnityEngine;

public class MyDeferredPublisher : MonoBehaviour
{
    private IDeferredBus _bus;

    private void Awake() => _bus = ServiceLocator.Resolve<IDeferredBus>();

    public void OnEnemyDied(int enemyId)
    {
        _bus.Enqueue(new EnemyDiedEvent(enemyId));   // queued — NOT published yet
    }

    private void LateUpdate()
    {
        _bus.Flush();   // publishes the batch through the Event Bus, in queue order
    }
}

Bootstrap は起動時にこのサービスを自動登録します。上のように、Awake() で一度解決してキャッシュしてください。

API リファレンス

IDeferredBus

メンバー説明
void Enqueue<TEvent>(TEvent evt) where TEvent : classevt を今キューに入れます。Flush() までパブリッシュされません。null イベントは即座に ArgumentNullException を投げるので、スタックトレースは呼び出し元を指します。class 制約はイベントバスと正確に一致します — struct イベントはサポートされません。IL2CPP/AOT ビルドで安全で、ボックス化は発生しません。
void Flush()キュー済みのバッチを、キュー順で、すべてのイベント型を横断して、イベント 1 件につきイベントバスの Publish 1 回でパブリッシュします。フラッシュの実行中にエンキューされたイベントは次のバッチに入ります。実行中のフラッシュの内側から Flush を呼んでも何もしません。
int PendingCount { get; }キュー済みでまだフラッシュされていないイベントの数。診断用。

DeferredBusOptions — 構築時の readonly struct

メンバー説明
int MaxDrainPerFlushFlush ごとにパブリッシュするイベント数の上限。0(デフォルト)は無制限。非ゼロの値(1–1,000,000 にクランプ)はその件数だけパブリッシュし、残りを次のバッチの先頭へ持ち越し、スロットルされた警告を 1 回ログします。
int InitialQueueCapacity内部バッファの初期サイズ。デフォルト 16、0–4096 にクランプ。
bool LogLifecycletrue のとき、Editor ビルドはキュー/フラッシュのトレースをログします。トレースはリリースビルドからは取り除かれます。デフォルト false
DeferredBusOptions(int maxDrainPerFlush, int initialQueueCapacity, bool logLifecycle = false)明示的コンストラクタ。数値範囲は構築時にクランプされます。
static DeferredBusOptions Default0 / 16 / falsedefault(DeferredBusOptions)(こちらは 0 / 0 / false)とは異なる点に注意してください。
DeferredBusOptions With(int? maxDrainPerFlush = null, int? initialQueueCapacity = null, bool? logLifecycle = null)名前付きの上書きによるイミュータブルなコピー。

使用例

using CommonGameSystem.Core;
using UnityEngine;

public sealed class DamageDealtEvent
{
    public int EnemyId { get; }
    public int Amount { get; }
    public DamageDealtEvent(int enemyId, int amount) { EnemyId = enemyId; Amount = amount; }
}

public class DamageResolver : MonoBehaviour
{
    private IDeferredBus _deferred;

    private void Awake() => _deferred = ServiceLocator.Resolve<IDeferredBus>();

    // Called from inside a physics callback — publishing immediately here could
    // re-enter the collection we are iterating. Queue it instead.
    public void OnHit(int enemyId, int amount)
    {
        _deferred.Enqueue(new DamageDealtEvent(enemyId, amount));
    }

    // Publish at a known-safe point: the end of the fixed-update step.
    private void FixedUpdate()
    {
        if (_deferred.PendingCount > 0)
            _deferred.Flush();   // every queued event publishes here, in queue order
    }
}

無効化する

ServiceLocator.Replace<IDeferredBus>(new NullDeferredBus());

これで遅延処理が完全にミュートされます。Enqueue はイベントを静かに捨て(実際のバスへ再ルーティングされることはありません)、Flush は何もせず、PendingCount0 のままです。実際の遅延キューに対して配線されたゲームは、遅延処理オフのまま動き続けます。イベントバスの無音な NullEventBus と異なり、この置き換えは構築時に 1 回警告をログします。静かに捨てるキュー(「イベントはキューされているようだが決して届かない」)は診断が難しいため、誤った差し替えを可視化しています。null イベントは、本物のサービスとまったく同じく引き続き例外を投げます。

よくある落とし穴

  • Flush するまで何も発火しません。 自動フラッシュはありません。キューに入れてフラッシュしなければ、イベントは決してパブリッシュされず、PendingCount は際限なく増えます。予測可能なドレインポイントを 1 つ — FixedUpdate の最後、フレームの最後 — 選び、必ずそこでフラッシュしてください。
  • フラッシュ中にキューされたイベントは、次のフラッシュを待ちます。 バッチは Flush が始まった瞬間に確定します。バッチのパブリッシュ中に購読者がエンキューしたものは、次の Flush まで保持されます。これは意図的です:どの 1 回のフラッシュも有界に保たれます。
  • MaxDrainPerFlush はバッチを分割します — イベントを捨てることはありません。 上限に達すると、残ったイベントは次のバッチの先頭へ移るので、後からキューされたものより先にパブリッシュされます。順序は保たれ、スロットルされた警告が 1 回ログされます。これは圧力弁であって、破棄ではありません。
  • フラッシュは決して例外を投げず、1 つの不良イベントが残りを止めることはできません。 Publish 呼び出しが例外を投げると、キューはそれを捕捉してログし、残りのイベントで続行します。フレームワーク自身のイベントバスは、欠陥のある購読者をすでに自分で隔離します。このガードは、そうしない置き換えバスをカバーするためのものです。

関連ページ

  • イベントバス — このキューがパブリッシュに使うルーティング層
  • スケジューラー — フラッシュポイントではなく、選んだ時刻に後からコールバックを実行する
  • ロガー — フラッシュの障害とライフサイクルトレースの行き先