最近注目を集めている MessagePipeを使ってみようと思いいろいろ調べて,自分なりにアクションゲームを作ってみました! (ゲームといってもMessagePipeを使うことが目的なので,ゲームにすらなっていないです)
成果物
Demo
MessagePipeを使ってアクションゲームのサンプルができた! pic.twitter.com/l9dn4zX2VS
— ようさん (@ayousanz) 2021年6月1日
キーボードのAを押すことで,左側のUnity chanに攻撃することができます
また,左のUnity chanからは定期的に(5秒後ごと)攻撃を受けます
どちらかのHPが0になったら勝敗モーションに移行後,ゲーム終了します
リポジトリ
リポジトリはpublicにしていますので,詳細はリポジトリをご確認ください
WebGL版
MessagePipeとは?
MessagePipe is a high-performance in-memory/distributed messaging pipeline for .NET and Unity. It supports all cases of Pub/Sub usage, mediator pattern for CQRS, EventAggregator of Prism(V-VM decoupling), etc.
- Dependency-injection first
- Filter pipeline
- better event
- sync/async
- keyed/keyless
- buffered/bufferless
- broadcast/response(+many)
- in-memory/distributed
MessagePipe is faster than standard C# event and 78 times faster than Prism's EventAggregator.
簡単に言うと,型でイベントの発行等を管理するライブラリみたいです(理解が怪しい)
環境
- Unity 2020.3.10f1
- VContainer v1.8.2
- MessagePipe v1.4.0
- UniRx v7.1.0
- UniTask v2.2.5
- Unity-Chan! Model
© Unity Technologies Japan/UCL
(注) VContainerとMessagePipeを使うときは,こちらのMessagePipe.VContainerを入れないと動かない?気がします
MessagePipeでの値の受け渡し方
関係する部分のみ記載しています.実際に使用方法は, #MessagePipe部分のコード もしくはリポジトリをご確認ください
イベントPublish側
//使用するPublisherとDisposeを定義 [Inject] private IPublisher<PlayerData> PlayerData { get; set; } private IDisposable _disposable; //イベントの発行 PlayerData.Publish(new PlayerData(){AttackValue = value,IsLose = isLose});
イベントSubscribe側
[Inject] private ISubscriber<EnemyData> EnemyData { get; set; } var d = DisposableBag.CreateBuilder(); EnemyData.Subscribe(e => { playerHp -= e.AttackValue; isPlay = e.IsLose; _animator.SetTrigger("IsDamage"); playerHpText.text = $"enemy hp:{playerHp}"; if (playerHp <= 0) { _animator.SetBool("IsLose",true); PublishData(0,true); } }).AddTo(d); _disposable = d.Build();
MessagePipe部分のコード
Player.cs
using System; using MessagePipe; using UnityEngine; using UnityEngine.UI; using VContainer; namespace Model { public class Player : MonoBehaviour { [SerializeField] private int playerHp; [SerializeField] private int attackValue; [SerializeField] private Text playerHpText; [Inject] private ISubscriber<EnemyData> EnemyData { get; set; } [Inject] private IPublisher<PlayerData> PlayerData { get; set; } private IDisposable _disposable; private Animator _animator; private bool isPlay; private void Awake() { playerHpText.text = $"player hp:{playerHp}"; _animator = GetComponent<Animator>(); } private void Start() { var d = DisposableBag.CreateBuilder(); EnemyData.Subscribe(e => { playerHp -= e.AttackValue; isPlay = e.IsLose; _animator.SetTrigger("IsDamage"); playerHpText.text = $"enemy hp:{playerHp}"; if (playerHp <= 0) { _animator.SetBool("IsLose",true); PublishData(0,true); } }).AddTo(d); _disposable = d.Build(); } private void Update() { if (Input.GetKeyDown(KeyCode.A) && !isPlay) { Debug.Log("プレイヤーが攻撃"); _animator.SetTrigger("IsAttack"); PublishData(3,false); } } private void PublishData(int value, bool isLose) { PlayerData.Publish(new PlayerData(){AttackValue = value,IsLose = isLose}); } private void OnDestroy() { _disposable?.Dispose(); } } }
Enemy.cs
using System; using MessagePipe; using UniRx; using UnityEngine; using UnityEngine.UI; using VContainer; using VContainer.Unity; namespace Model { public class Enemy : MonoBehaviour,IStartable { [SerializeField] private int enemyHp; [SerializeField] private Text enemyHpText; private bool _isPlay; [Inject] private ISubscriber<PlayerData> PlayerData { get; set; } [Inject] private IPublisher<EnemyData> EnemyData { get; set; } private Animator _animator; private IDisposable _disposable; private void Awake() { enemyHpText.text = $"enemy hp:{enemyHp}"; _animator = GetComponent<Animator>(); } public void Start() { var d = DisposableBag.CreateBuilder(); PlayerData.Subscribe(e => { enemyHp -= e.AttackValue; _isPlay = e.IsLose; _animator.SetTrigger("IsDamage"); enemyHpText.text = $"enemy hp:{enemyHp}"; if (enemyHp <= 0) { PublishData(0,true); _animator.SetBool("IsLose",true); } }).AddTo(d); _disposable = d.Build(); Observable.Interval(TimeSpan.FromSeconds(5f)) .Where(_=>!_isPlay) .Subscribe(_ => { Debug.Log("敵が攻撃"); _animator.SetTrigger("IsAttack"); PublishData(3,false); }).AddTo(this); } private void PublishData(int value, bool isLose) { EnemyData.Publish(new EnemyData(){AttackValue = value,IsLose = isLose}); } private void OnDestroy() { _disposable?.Dispose(); } } }
DI(VContainer)
using MessagePipe; using Model; using VContainer; using VContainer.Unity; public class GameLifetimeScope : LifetimeScope { protected override void Configure(IContainerBuilder builder) { var options = builder.RegisterMessagePipe(); builder.RegisterMessageBroker<PlayerData>(options); builder.RegisterMessageBroker<EnemyData>(options); } }
受け渡しているデータクラス
namespace Model { public class Data { public int AttackValue; public bool IsLose; } public class PlayerData : Data { } public class EnemyData : Data { } }