ゲーム開発
Unity
UnrealEngine
C++
Blender
ゲーム数学
ゲームAI
グラフィックス
サウンド
アニメーション
GBDK
制作日記
IT関連
ツール開発
フロントエンド関連
サーバサイド関連
WordPress関連
ソフトウェア設計
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
ラーメン日記
四コマ漫画
その他
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • Blender
    • ゲーム数学
    • ゲームAI
    • グラフィックス
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • IT関連
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • WordPress関連
    • ソフトウェア設計
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • ラーメン日記
    • 四コマ漫画
      • その他
        • おすすめアイテム
        • おもしろコラム
      1. ホーム
      2. 20221015_01

      【Unity】MessagePipeの基本的な使い方についてまとめる

      UnityMessagePipe
      2022-10-15

      マイケル
      マイケル
      みなさんこんにちは!
      マイケルです!
      エレキベア
      エレキベア
      こんにちクマ〜〜〜
      マイケル
      マイケル
      今日はUnityのメッセージングライブラリである、
      MessagePipe の使い方について紹介します!
      エレキベア
      エレキベア
      最近よく聞くやつクマね
      マイケル
      マイケル
      UniRxと結局何が違うのか、といった点も踏まえてみていくので
      気になっている方もぜひご参照ください!
      エレキベア
      エレキベア
      やったるクマ〜〜〜

      MessagePipeとは

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

      GitHub – Cysharp/MessagePipe

      MessagePipe – .NET と Unityのためのハイパフォーマンスメッセージングライブラリ

      マイケル
      マイケル
      これはいわゆる Publish/Sublish(Pub/Sub)パターン と呼ばれるもので、
      下記のようにメッセージを送る仲介者の存在を知らずにやり取りを行うことができます。
      ↑Pub/Subパターン
      エレキベア
      エレキベア
      ObserverパターンやUniRxだと基本的に発行者への依存が発生してしまうクマから、
      お互いを完全に知らずにやり取りできるのは疎結合になってよさそうクマね
      マイケル
      マイケル
      ただその分、どんなに離れていてもやり取りできてしまうから、
      むやみに使いすぎるとどこでやり取りしているのかの管理が大変になってしまう点には注意が必要だね!
      エレキベア
      エレキベア
      とりあえずこのPub/SubパターンをUnityで実装できるという点が
      MessagePipeの利点なのクマね
      マイケル
      マイケル
      ちなみにUniRxにも同じようにPub/Subパターンを実装できる
      MessageBrokerという機能があるんだ。

      UniRxのMessageBrokerが便利という話 – Qiita

      エレキベア
      エレキベア
      え、UniRxにもあるクマか・・・
      ならわざわざMessagePipeを使う理由って何なのクマ・・・
      マイケル
      マイケル
      MessagePipeはDIコンテナの導入が前提になっているから、
      DIコンテナを通すことである程度メッセージできる範囲を制御できるというのが大きなメリットかなと思っているよ。
      これでさっき話したどこでも呼び出せるという問題が軽減されるからね。
      エレキベア
      エレキベア
      なるほどクマ
      DIコンテナとも連携してPub/Subを使いたい場合に有効なのクマね
      マイケル
      マイケル
      まあそもそもUniRxは多機能すぎるから、シンプルに使いたいというだけでもいいかもね!
      それじゃ早速使い方をみていこう!
      エレキベア
      エレキベア
      楽しみクマ〜〜〜

      MessagePipeの使い方

      導入方法

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

      GitHub – Cysharp/MessagePipe#Unity

      マイケル
      マイケル
      まず、MessagePipeを使用するにあたり
      ・DIライブラリ(VContainer or Zenject)
      ・UniTask

      の導入が前提になっているため、これらをUnityにインストールします。
      今回はVContainerを使用して進めました。

      インストール | VContainer

      GitHub – Cysharp/UniTask

      エレキベア
      エレキベア
      もうもはや使用するのが定番になっているクマからね
      マイケル
      マイケル
      準備ができたら、MessagePipeのライブラリを入れていきます。
      こちらは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型のメッセージを登録していますが、独自クラスでも指定することができます。
      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でイベントを発行する、といったものになります。
      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の基本的な使い方についてまとめる 〜完〜


      UnityMessagePipe
      2022-10-15

      関連記事
      【Unity】Timeline × Excelでスライドショーを効率よく制作する
      2024-10-31
      【Unity】Boidsアルゴリズムを用いて魚の群集シミュレーションを実装する
      2024-05-28
      【Unity】GoでのランキングAPI実装とVPSへのデプロイ方法についてまとめる【Go言語】
      2024-04-14
      【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-30
      【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
      2024-03-30
      【Unity】第二回 CRI ADXを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-28
      【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜
      2024-03-28
      【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
      2024-03-27