MessagePipe (+Zenject)에서 우선 이벤트를 보내고 받았습니다.

17299 단어 MessagePipeC#Unity
Cysharp가 공개한 MessagePipe입니다만 「개인적으로 UniTask, UniRx와 같이 사용하고 싶다!」
그래서 조사한 내용을 조금씩 기사로 해 나갈 것입니다.

다음 개발 환경에서 MessagePipe를 사용하고 있습니다.
  • Unity
  • Zenject
  • MessgePipe v1.6.1
  • MessagePipeとは?라는 곳은 다른 쪽이 훌륭한 기사를 들 수 있으므로 여기에서는 쓰지 않습니다.Zenject에 대해서도 마찬가지

    다음 기사는 MessagePipe에 대해 자세히 설명합니다!

    이번에는 Zenject, Unity 환경에서 우선 이벤트를 송수신한다.
    라는 부분만의 기사입니다

    UnityTestRunner로 만져보세요



    갑작스럽지만 "기억하기 위해서는 실천한다"
    우선 UnityTestRunner에서 만나 보겠습니다.

    MessagePipe와 Zenject의 조합으로 UnityTestRunner를 실행해 보았습니다.
    using NUnit.Framework;
    using MessagePipe;
    using Zenject;
    using UnityEngine;
    
    
    public class Test
    {
        // イベント送る方
        public class Publisher
        {
            [Inject] private IPublisher<MyEvent> _publisher;
    
            public void Send(MyEvent ev) =>
                _publisher.Publish(ev);
        }
    
        // イベント受け取る方
        public class Subscriber
        {
            [Inject] private ISubscriber<MyEvent> _subscriber;
    
            public void Setup() =>
                _subscriber.Subscribe(x => Debug.Log($"{x.Message}"));
        }
    
        // 送るイベント
        public class MyEvent 
        { 
            public string Message; 
        }
    
    
        private DiContainer _container;
    
        [Test]
        public void SimpleTest()
        {
            _container = new DiContainer();
    
            _container.BindMessageBroker<MyEvent>(_container.BindMessagePipe());
    
            // イベントを受ける方
            var sub = _container.Instantiate<Subscriber>();
            sub.Setup();
    
            // イベントを投げる方
            var publisher = _container.Instantiate<Publisher>();
            publisher.Send(new MyEvent { Message = "テストメッセージ" });
        }
    }
    

    이제 테스트 메시지라는 문자열이 로그에 표시되었습니다.


    흐름
    1. Zenject DIContainer 생성
    2. BindMessageBroker<MyEvent>(_container.BindMessagePipe());에서 MyEvent 이벤트를 보내고 받을 수 있도록 허용
    3. _container.Instantiate에서 Publisher와 Subscriber를 생성하면서 Inject도 받는다
    4. Publisher에서 메시지를 보내면 Subscriber가 반응하여 DebugLog가 표시된다

    엄청 심플합니다만 이 코드만으로 이벤트의 송수신을 할 수 있었습니다.
    MyEvent라는 것을 조금 더 현실감이 있습니다.
    public class LogEvent
    {
        public string logMessage;
        public ErrorType errorType;
    }
    


    // 弾が命中したとき
    public class BulletHitEvent
    {
        public Enemy enemy; // 命中した敵
        public int damage; // あたったときのダメージ量
    }
    

    하여 이 이벤트를 송수신할 수 있도록 합니다.
    // まずはイベント登録
    // どこかのSceneContext
    public class SceneContextInstaller : MonoInstaller
    {
        public override void InstallBindings()
        {
            var option = Container.BindMessagePipe();
    
            // BindMessageBrokerでイベント登録
            Container.BindMessageBroker<LogEvent>(option);
            Container.BindMessageBroker<BulletHitEvent>(option);
        }
    }
    

    실제로 이벤트를 송수신하는 클래스
    // 弾クラス
    public class Bullet
    {
        // Zenjectが勝手にInjectしてくれる
        [Inject] IPublisher<BulletHitEvent> _bulletHitEvent;
        [Inject] IPublisher<LogEvent> _logEvent; 
    
        // 敵にあたったとき呼び出される
        public void Hit(Enemy enemy)
        {
            _bulletHitEvent.Publish(new BulletHitEvent { enemy = enemy, damage = 10 };
        }
    
        // なにか致命的なエラーが起きたときなど
        public void NanrakanoError()
        {
            _logEvent.Publish(new LogEvent 
                { 
                     logMessage = "何らかのエラーが起きました", 
                     errorType = ErrorType.Error 
                };
        }
    }
    
    ....
    
    // どこかのSceneでログを受け取る
    public class Scene
    {
        // Zenjectが勝手にInjectしてくれる
        [Inject] ISubscriber<BulletHitEvent> _bulletHitEvent;
        [Inject] ISubscriber<LogEvent> _logEvent; 
    
        public void Setup()
        {
          // Subscribeしてイベントを受け取る
          _bulletHitEvent.Subscribe(....);
          _logEvent.Subscribe(....);
        }
    }
    

    등등..

    조금씩 코드에 통합함으로써
    이벤트 구동을 사용하는 장면도 서서히 떠올라갈까 생각합니다

    GlobalMessagePipe로 메시지 송수신



    Zenject를 사용하여 MessagePipe를 이용하기 때문에 이벤트의 범위도 Zenject가 Inject 할 수 있는 범위와 동일하다.
    라고 생각됩니다.
    SceneContext가 이벤트가 송수신할 수 있는 범위입니다.
    SceneContext에 관계없이 프로젝트 전체에서 이벤트를 보내고 받을 수 있도록 하려면 ProjectContext의 Installer에서 BindMessageBroker를 사용하는 것이 좋습니다.

    그리고 좀 더 간편하게 메시지의 송수신을 할 수 있도록 GlobalMessagePipe를 사용해 보겠습니다.

    GlobalMessagePipe는 이름 그대로 글로벌 공간에서 사용할 수 있는 것입니다.
    using Zenject;
    using MessagePipe;
    
    namespace XXX
    {
        // ProjectContext.Prefabにこのスクリプトをアタッチ
        public class ProjectContextInstaller : MonoInstaller
        {
            public override void InstallBindings()
            {
                var option = Container.BindMessagePipe();
    
                // LogEventをProjectContextに登録。これでどこでもLogEventが投げられるようになる
                Container.BindMessageBroker<LogEvent>(option);
    
                // GlobalMessagePipeを使用する前にSetProviderに設定する必要がある
                GlobalMessagePipe.SetProvider(Container.AsServiceProvider());
            }
        }
    }
    

    이제 GlobalMessagePipe를 사용할 수 있습니다.
    사용하기 위해서는 GlobalMessagePipe에 DIContainer의 ServiceProvider를 등록해야 합니다만, 여기에 등록하는 것이

    ProjectContext는 장면에 관계없이 살아있는 것이므로 GlobalMessagePipe에 등록하는 것으로는 적절할까 생각했습니다.

    등록한 후는 GlobalMessagePipe의 정적 메소드로 부담없이 메시지의 송수신을 할 수 있습니다.
    // Manager的な人がゲーム全体で発行されたLogEventを受信する
    public class Manager : Monobehaviour
    {
        public void Awake()
        {
            // GlobalMessagePipeから直接LogEventのSubscriberを取得できる。そして即購読
            var subscriber = GlobalMessagePipe.GetSubscriber<LogEvent>();
            subscriber.Subscribe(ev => 
            {
                // コンソールに出したり、通信でログサーバーに送ったり
            });
        }
    }
    
    // バトルシーン
    public class BattleScene
    {
        public void NanrakanoError()
        {
            // 同じようにPublisherも直接取得して即時イベント発行
            var publisher = GlobalMessagePipe.GetPublisher<LogEvent>();
            _logEvent.Publish(new LogEvent 
                { 
                     logMessage = "何らかのエラーが起きました", 
                     errorType = ErrorType.Error 
                };
        }
    }
    

    이것으로 더욱 부담없이 메시지의 송수신을 할 수 있게 되었습니다.
    이벤트는 장소를 신경쓰지 않고 순간적으로 발행하고 싶은 것이 있기 때문에 비교적 이쪽이 실제로는 사용하는 케이스가 많은 것은 아닐까요

    끝에



    Dispose나 필터 기능 등 기타 편리한 기능에 대해서는 다루지 않습니다.
    메시지를 송수신하는 것 뿐만이 아니라 MessagePipe에 갖추어져 있는 기능을 잘 다루는 것으로 한층 더 게임 개발 효율의 향상을 할 수 있다고 생각합니다.

    또 실제로 사용해 눈치채는 일이나 편리한 내용은 기사로 해 갑니다.

    다음 Disopose 주위에 대한 기사

    좋은 웹페이지 즐겨찾기