ホーム
ゲーム開発
Unity
UnrealEngine
C++
ゲーム数学
ゲームAI
サウンド
アニメーション
GBDK
制作日記
3DCG
Houdini
Blender
USD
グラフィックス
テクノロジ
生成AI関連
動画制作
ツール開発
フロントエンド関連
サーバサイド関連
ソフトウェア設計
ハードウェア関連
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
その他
ブログについて
都会のエレキベア
ラーメン日記
四コマ漫画
おすすめアイテム
おもしろコラム
お問い合わせ
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • ゲーム数学
    • ゲームAI
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • 3DCG
    • Houdini
    • Blender
    • USD
    • グラフィックス
  • テクノロジ
    • 生成AI関連
    • 動画制作
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • ソフトウェア設計
    • ハードウェア関連
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • その他
JP|EN
  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
記事をSNSで共有する
X
Facebook
LINE
はてなブックマーク
Pocket
LinkedIn
Reddit

著者の各種アカウント
フォローいただけると大変励みになります!

関連記事
【Unity】Light2DとTimelineを活用した2Dライブシーンの構築
2026-07-20
【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