🍣
Zenject.Signalsを使う
Zenject.Signalsとは
- Zenjectの一機能
- UniRxのMessageBrokerのようなPub/Subメッセージングモデル
- UniRxとも親和性がある
- 尚、本稿で扱うのはVersion 9.2.0とする
基本の使い方
PubからSubにメッセージを送る
- Installerで
SignalBus
のインストール・メッセージの宣言を行う - Sub側はメッセージを受け取るメソッドをバインドする
- Pub側では
SignalBus
を持ち、任意のタイミングでSignalBus.Fire
を発火
public class Publish : MonoBehaviour
{
[Inject]
private SignalBus signalBus = default;
private void Start()
{
signalBus.Fire(new MessageSignal { Message = "Fire" });
}
}
public class MessageSignal
{
public string Message;
}
public class MessageSubscriber
{
public void RecieveMessage(MessageSignal messageSignal)
{
Debug.Log(messageSignal.Message);
}
}
public class Installer : MonoInstaller
{
public override void InstallBindings()
{
SignalBusInstaller.Install(Container);
Container.DeclareSignal<MessageSignal>();
Container.Bind<MessageSubscriber>().AsSingle();
Container.BindSignal<MessageSignal>()
.ToMethod<MessageSubscriber>(x => x.RecieveMessage).FromResolve();
}
}
メッセージをSignalBus経由で受け取る
- メッセージを受け取るようにできたが、受け取るメソッドをインストーラに記述したくない場合もある
-
SignalBus.Subscribe
/SignalBus.Unsubscribe
を使用し、 メッセージを受け取るメソッドを登録/解除する
public class Publish : MonoBehaviour
{
[Inject]
private SignalBus signalBus = default;
private void Start()
{
signalBus.Fire(new MessageSignal { Message = "Fire" });
}
}
public class MessageSignal
{
public string Message;
}
public class MessageSubscriber: MonoBehaviour
{
[Inject]
private SignalBus signalBus = default;
private void Start()
{
signalBus.Subscribe<MessageSignal>(RecieveMessage);
}
private void OnDestroy()
{
signalBus.Unsubscribe<MessageSignal>(RecieveMessage);
}
public void RecieveMessage(MessageSignal messageSignal)
{
Debug.Log(messageSignal.Message);
}
}
public class Installer : MonoInstaller
{
public override void InstallBindings()
{
SignalBusInstaller.Install(Container);
Container.DeclareSignal<MessageSignal>();
}
}
メッセージの受け取りをUniRx経由にする
- Zenject.SignalsでUnirxを使用できるように設定する必要がある
- ZenjectのAssembly DefinitionのAssembly Definition References にUniRxを追加し、 Apply
- Edit > Player Settings > Player > Scripting Define Symbolsに
ZEN_SIGNALS_ADD_UNIRX
を追加
-
SignalBus.GetStream
経由でSubscribeする
public class Publish : MonoBehaviour
{
[Inject]
private SignalBus signalBus = default;
private void Start()
{
signalBus.Fire(new MessageSignal { Message = "Fire" });
}
}
public class MessageSignal
{
public string Message;
}
public class MessageSubscriber: MonoBehaviour
{
[Inject]
private SignalBus signalBus = default;
private void Start()
{
signalBus.GetStream<MessageSignal>()
.Subscribe(messageSignal => Debug.Log(messageSignal.Message))
.AddTo(this);
}
}
public class Installer : MonoInstaller
{
public override void InstallBindings()
{
SignalBusInstaller.Install(Container);
Container.DeclareSignal<MessageSignal>();
}
}
メッセージの宣言について
Container.DeclareSignal<SignalType>()
.WithId(Identifier)
.(RequireSubscriber|OptionalSubscriber|OptionalSubscriberWithWarning)()
.(RunAsync|RunSync)()
.WithTickPriority(TickPriority)
.(Copy|Move)Into(All|Direct)SubContainers();
- SignalType - メッセージを表す型
- Identifier - 識別子。Bindなどと同様に一意の値を指定して同様の型が宣言されていても場合分けすることが可能になる。e.g. SignalTypeがstring型など
- RequireSubscriber/OptionalSubscriber/OptionalSubscriberWithWarning - メッセージの受取先が全く無い場合の振る舞いの設定
- RequireSubscriber - 例外をスロー
- OptionalSubscriber - とくになし
- OptionalSubscriberWithWarning - 警告をコンソールに出力
- デフォルトは
Container.Settings.Signals.MissingHandlerDefaultResponse
で設定できるのでProjectInstallerなどで設定すると良い。 尚、MissingHandlerDefaultResponse
のデフォルトは公式ドキュメントを見る限りだとOptionalSubscriber
となっているが現バージョンだとOptionalSubscriberWithWarning
となっている模様 - RunAsync/RunSync - メッセージの受け取りを同期的に行うか、非同期的に行うか。デフォルトはRunSync
-
RunSync -
SignalBus.Fire
されたときに直ちにすべてのSubscribe先のメソッドが呼び出される -
RunAsync - 後述の
TickPriority
で指定されたタイミングで呼び出す -
TickPriority -
RunAsync
設定時のみ有効。メソッドが呼び出される優先度を指定 - (Copy|Move)Into(All|Direct)SubContainers - 通常のBind同様に殆どの場合無視できる。SubContainerに対して一部Bindしたくないときなどに使用
BindSignalについて
Container.BindSignal<SignalType>()
.WithId(Identifier)
.ToMethod(Handler)
.From(ConstructionMethod)
.(Copy|Move)Into(All|Direct)SubContainers();
- SignalType- 受け取るメッセージを表す型
- Identifier - メッセージの宣言についてと同上
-
ConstructionMethod - インスタンスの取得元の定義。
FromResolve
From
FromNew
など - **(Copy|Move)Into(All|Direct)**SubContainers - メッセージの宣言についてと同上
- Handler メッセージを受け取ったときに呼び出されるメソッドの指定
Abstract Signals
- Version9.2.0 で追加された機能
- メッセージにInterfaceを複数定義しておき、受ける側はInterfaceで受け取るようにすることで一度のFireで様々なメッセージを同時に発火できる
- メッセージの宣言についての項でも記載したが
OptionalSubscriberWithWarning
がデフォルトになっているようなので 意図しないところでSubするべき型がなくて警告が出ることがあるのでontainer.Settings.Signals.MissingHandlerDefaultResponse
をOptionalSubscriber
にして運用するなどが良いように思える
public class Example
{
SignalBus signalBus;
public Example(Signalbus signalBus) => this.signalBus = signalBus;
public void CheckpointReached() => signalBus.AbstractFire<SignalCheckpointReached>();
public void DestroyWorld() => signalBus.AbstractFire<SignalWorldDestroyed>();
}
public class SoundSystem
{
public SoundSystem(SignalBus signalBus)
{
signalBus.Subscribe<ISignalSoundPlayer>(x => PlaySound(x.soundId));
}
void PlaySound(int soundId) { PlaySE(soundId);}
}
public class AchievementSystem
{
public AchievementSystem(SignalBus signalBus)
{
signalBus.Subscribe<ISignalAchievementUnlocker>(x => UnlockAchievement(x.achievementKey));
}
void UnlockAchievement(string key) { Debug.Log("Unlocks the achievement with the given key" + key); }
}
public class Installer : MonoInstaller
{
public override void InstallBindings()
{
Container.DeclareSignalWithInterfaces<SignalCheckpointReached>();
Container.DeclareSignalWithInterfaces<SignalWorldDestroyed>();
}
}
// signal types
public struct SignalCheckpointReached : ISignalGameSaver, ISignalSoundPlayer
{
public int SoundId { get => 2}
}
public struct SignalWorldDestroyed : ISignalAchievementUnlocker, ISignalSoundPlayer
{
public int SoundId { get => 4}
public string AchievementKey { get => "WORLD_DESTROYED"}
}
// signal interfaces
public interface ISignalGameSaver{}
public interface ISignalSoundPlayer{ int SoundId {get;}}
public interface ISignalAchievementUnlocker{ string AchievementKey {get;}}
Discussion