
マイケル
みなさんこんにちは!
マイケルです!
マイケルです!

エレキベア
こんにちクマ〜〜〜

マイケル
今日はUnityのメッセージングライブラリである、
MessagePipe の使い方について紹介します!
MessagePipe の使い方について紹介します!

エレキベア
最近よく聞くやつクマね

マイケル
UniRxと結局何が違うのか、といった点も踏まえてみていくので
気になっている方もぜひご参照ください!
気になっている方もぜひご参照ください!

エレキベア
やったるクマ〜〜〜
MessagePipeとは

マイケル
まずMessagePipeとは何かについてですが、こちらはCysharpさんが公開しているUnity用のメッセージングライブラリになります!
MessagePipe – .NET と Unityのためのハイパフォーマンスメッセージングライブラリ

マイケル
これはいわゆる Publish/Sublish(Pub/Sub)パターン と呼ばれるもので、
下記のようにメッセージを送る仲介者の存在を知らずにやり取りを行うことができます。
下記のようにメッセージを送る仲介者の存在を知らずにやり取りを行うことができます。


エレキベア
ObserverパターンやUniRxだと基本的に発行者への依存が発生してしまうクマから、
お互いを完全に知らずにやり取りできるのは疎結合になってよさそうクマね
お互いを完全に知らずにやり取りできるのは疎結合になってよさそうクマね

マイケル
ただその分、どんなに離れていてもやり取りできてしまうから、
むやみに使いすぎるとどこでやり取りしているのかの管理が大変になってしまう点には注意が必要だね!
むやみに使いすぎるとどこでやり取りしているのかの管理が大変になってしまう点には注意が必要だね!

エレキベア
とりあえずこのPub/SubパターンをUnityで実装できるという点が
MessagePipeの利点なのクマね
MessagePipeの利点なのクマね

マイケル
ちなみにUniRxにも同じようにPub/Subパターンを実装できる
MessageBrokerという機能があるんだ。
MessageBrokerという機能があるんだ。
UniRxのMessageBrokerが便利という話 – Qiita

エレキベア
え、UniRxにもあるクマか・・・
ならわざわざMessagePipeを使う理由って何なのクマ・・・
ならわざわざMessagePipeを使う理由って何なのクマ・・・

マイケル
MessagePipeはDIコンテナの導入が前提になっているから、
DIコンテナを通すことである程度メッセージできる範囲を制御できるというのが大きなメリットかなと思っているよ。
これでさっき話したどこでも呼び出せるという問題が軽減されるからね。
DIコンテナを通すことである程度メッセージできる範囲を制御できるというのが大きなメリットかなと思っているよ。
これでさっき話したどこでも呼び出せるという問題が軽減されるからね。

エレキベア
なるほどクマ
DIコンテナとも連携してPub/Subを使いたい場合に有効なのクマね
DIコンテナとも連携してPub/Subを使いたい場合に有効なのクマね

マイケル
まあそもそもUniRxは多機能すぎるから、シンプルに使いたいというだけでもいいかもね!
それじゃ早速使い方をみていこう!
それじゃ早速使い方をみていこう!

エレキベア
楽しみクマ〜〜〜
MessagePipeの使い方
導入方法

マイケル
それでは早速導入していきます。
導入方法は今後変更される可能性もあるため、基本的には最新のREADMEを参考に進めましょう!
導入方法は今後変更される可能性もあるため、基本的には最新のREADMEを参考に進めましょう!
GitHub – Cysharp/MessagePipe#Unity

マイケル
まず、MessagePipeを使用するにあたり
・DIライブラリ(VContainer or Zenject)
・UniTask
の導入が前提になっているため、これらをUnityにインストールします。
今回はVContainerを使用して進めました。
・DIライブラリ(VContainer or Zenject)
・UniTask
の導入が前提になっているため、これらをUnityにインストールします。
今回はVContainerを使用して進めました。

エレキベア
もうもはや使用するのが定番になっているクマからね

マイケル
準備ができたら、MessagePipeのライブラリを入れていきます。
こちらはCoreとなるライブラリと使用するDIライブラリに対応したライブラリを入れる必要があります。
今回は下記の2つが対象になりますね。
こちらはCoreとなるライブラリと使用するDIライブラリに対応したライブラリを入れる必要があります。
今回は下記の2つが対象になりますね。
・Coreライブラリ
https://github.com/Cysharp/MessagePipe.git?path=src/MessagePipe.Unity/Assets/Plugins/MessagePipe
・VContainer用
https://github.com/Cysharp/MessagePipe.git?path=src/MessagePipe.Unity/Assets/Plugins/MessagePipe.VContainer

マイケル
これらを導入すれば、MessagePipeを使用する準備は完了です!!

エレキベア
やったるクマ〜〜〜
基本的な使い方

マイケル
まずは基本的な使い方をみていきましょう。
LifetimeScopeでのメッセージ登録は、下記のようにoptionを作成してRegisterMessageBrokerで登録します。
今回はint型のメッセージを登録していますが、独自クラスでも指定することができます。
LifetimeScopeでのメッセージ登録は、下記のようにoptionを作成してRegisterMessageBrokerで登録します。
今回はint型のメッセージを登録していますが、独自クラスでも指定することができます。
using MessagePipe;
using VContainer;
using VContainer.Unity;
public class TestLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
// int型のメッセージを登録してみる
var options = builder.RegisterMessagePipe();
builder.RegisterMessageBroker<int>(options);
// EntryPoint登録
builder.RegisterEntryPoint<TestEntryPoint>();
}
}
↑int型のメッセージを登録
マイケル
あとは発行側(Publisher)、購読側(Subscriber)のイベントそれぞれを受け取ってメッセージング処理を実装します。
使い方としては下記のように、Subscriverに受け取った際の処理を記述し、Publisherでイベントを発行する、といったものになります。
使い方としては下記のように、Subscriverに受け取った際の処理を記述し、Publisherでイベントを発行する、といったものになります。
using System;
using Cysharp.Threading.Tasks;
using MessagePipe;
using UnityEngine;
using VContainer.Unity;
public class TestEntryPoint : IStartable, IDisposable
{
private readonly IPublisher<int> _testPublisher;
private readonly ISubscriber<int> _testSubscriber;
private IDisposable _disposable;
public TestEntryPoint(IPublisher<int> testPublisher, ISubscriber<int> testSubscriber)
{
_testPublisher = testPublisher;
_testSubscriber = testSubscriber;
}
public void Start()
{
// 購読開始
var d = DisposableBag.CreateBuilder();
_testSubscriber.Subscribe(x =>
{
Debug.Log(x); // とりあえずログ出力
}).AddTo(d);
_disposable = d.Build();
// 試しに発行
TestPublishAsync();
}
public void Dispose()
{
// 購読解除
_disposable?.Dispose();
}
private async void TestPublishAsync()
{
// 一定間隔で発行する
_testPublisher.Publish(10);
await UniTask.Delay(500);
_testPublisher.Publish(20);
await UniTask.Delay(500);
_testPublisher.Publish(30);
}
}
↑簡単なPub/Sub実装
マイケル
Subscriberに購読処理を設定した際にはDisposableも設定して、必ず購読解除の処理も実装するようにしましょう。

エレキベア
非同期処理のお約束クマね

マイケル
この状態でLifetimeScopeをアタッチして実行すると、
正常にメッセージング処理が行われることが確認できました!
正常にメッセージング処理が行われることが確認できました!


エレキベア
シンプルで使いやすいクマね
攻撃通知を送るサンプル

マイケル
ここからもう一歩踏み込んで、攻撃通知を送るサンプルを作成してみます。
下記のような攻撃データをやり取りする例になります。
下記のような攻撃データをやり取りする例になります。
/// <summary>
/// 攻撃データ
/// </summary>
public class AttackData
{
/// <summary>
/// ダメージ量
/// </summary>
public int Damage;
}
↑送信する攻撃データ
エレキベア
ゲームではよくある実装クマね

マイケル
プレイヤーを発行者(Publisher)、エネミーを購読者(Subscriber)として、エネミー全体へ攻撃通知するといった内容で実装します。
using MessagePipe;
/// <summary>
/// プレイヤークラス
/// </summary>
public class Player
{
private readonly IPublisher<AttackData> _onDoAttackEvent;
public Player(IPublisher<AttackData> onDoAttackEvent)
{
_onDoAttackEvent = onDoAttackEvent;
}
/// <summary>
/// 攻撃実行
/// </summary>
public void OnAttack(int damage)
{
_onDoAttackEvent.Publish(new AttackData()
{
Damage = damage,
});
}
}
↑プレイヤーから攻撃イベントを発行するusing System;
using MessagePipe;
using UnityEngine;
/// <summary>
/// エネミークラス
/// </summary>
public class Enemy : IDisposable
{
private readonly ISubscriber<AttackData> _onReceiveAttackEvent;
private IDisposable _disposable;
private string _name;
private int _hp;
private bool _isDead;
public Enemy(ISubscriber<AttackData> onReceiveAttackEvent, string name, int hp)
{
_onReceiveAttackEvent = onReceiveAttackEvent;
_name = name;
_hp = hp;
_isDead = false;
OnInitialize();
}
/// <summary>
/// 初期化処理
/// </summary>
private void OnInitialize()
{
// 攻撃通知イベントの購読開始
var d = DisposableBag.CreateBuilder();
_onReceiveAttackEvent.Subscribe(attackData =>
{
if (_isDead) return;
// ダメージを受ける
_hp -= attackData.Damage;
Debug.Log($"{_name} は {attackData.Damage} のダメージを受けた");
// 死亡判定
if (_hp <= 0)
{
_hp = 0;
_isDead = true;
Debug.Log($"{_name} は死亡した...");
}
}).AddTo(d);
_disposable = d.Build();
}
public void Dispose()
{
_disposable?.Dispose();
}
}
↑エネミーは攻撃イベントを受け取ったらダメージを受ける
エレキベア
使い方としてはさっきの例と変わらないクマね

マイケル
あとはLifetimeScopeにメッセージを登録して、プレイヤーやエネミーの生成処理を書けば完了です!
今回は下記のようにしてみました。
今回は下記のようにしてみました。
using MessagePipe;
using VContainer;
using VContainer.Unity;
public class BattleLifeTimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
// 攻撃通知のメッセージを登録
var options = builder.RegisterMessagePipe();
builder.RegisterMessageBroker<AttackData>(options);
// EntryPoint登録
builder.RegisterEntryPoint<BattleEntryPoint>();
}
}
↑メッセージの登録using System.Collections.Generic;
using MessagePipe;
using UnityEngine;
using VContainer.Unity;
public class BattleEntryPoint : IStartable, ITickable
{
private readonly IPublisher<AttackData> _onDoAttackEvent;
private readonly ISubscriber<AttackData> _onReceiveAttackEvent;
public BattleEntryPoint(IPublisher<AttackData> onDoAttackEvent, ISubscriber<AttackData> onReceiveAttackEvent)
{
_onDoAttackEvent = onDoAttackEvent;
_onReceiveAttackEvent = onReceiveAttackEvent;
}
private Player _player;
private List<Enemy> _enemies;
public void Start()
{
// プレイヤー生成
_player = new Player(_onDoAttackEvent);
// エネミー生成
_enemies = new List<Enemy>();
_enemies.Add(new Enemy(_onReceiveAttackEvent, "エレキベア", 200));
_enemies.Add(new Enemy(_onReceiveAttackEvent, "ゴロヤン", 500));
_enemies.Add(new Enemy(_onReceiveAttackEvent, "パインバード", 100));
}
public void Tick()
{
// スペースキー押下で攻撃
if (Input.GetKeyDown(KeyCode.Space))
{
_player.OnAttack(100);
}
}
}
↑プレイヤー、エネミーの生成処理
マイケル
LifetimeScopeをアタッチして何回か攻撃してみます。
正常に攻撃通知できていることが確認できますね!
正常に攻撃通知できていることが確認できますね!


エレキベア
これでバッチリクマ〜〜〜
おわりに

マイケル
というわけでMessagePipeの使い方でした!
どうだったかな??
どうだったかな??

エレキベア
シンプルで使いやすくていいライブラリだと思ったクマ〜〜〜

マイケル
Pub/Sub実装は手軽に使えるから今後使っていきたいね!
それでは今日はこの辺で!アデューー!!!
それでは今日はこの辺で!アデューー!!!

エレキベア
クマ〜〜〜〜〜
【Unity】MessagePipeの基本的な使い方についてまとめる 〜完〜
コメント