重构消息系统,不以接口为单位发送消息,而是使用Action。这样用来减少发消息的粒度,想象一下,我们有一个控制类监听了WASD四个按键,但是这个控制类只有一个方法实现IReceiver
接口,我们还需要在方法内判断按下的键是什么,这样有损性能。能不判断就不判断是程序底层设计时的优化原则之一。
直接使用Action多播,会有重复监听的问题,我们在最后实现一个ActionMgr
,使用HashSet<Action<T>>
来解决这个问题。
修改MessageSystem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| using System; using System.Collections.Generic;
public interface IMessageSystem { void AddListener(int key, Action<object[]> callback); void RemoveListener(int key, Action<object[]> callback); void DispatchMsg(int key, params object[] args);
void AddListener(string key, Action<object[]> callback); void RemoveListener(string key, Action<object[]> callback); void DispatchMsg(string key, params object[] args); }
public class MessageSystem : IMessageSystem { private readonly Dictionary<int, Action<object[]>> _intReceivers = new(); private readonly Dictionary<string, Action<object[]>> _stringReceivers = new(); public void AddListener(int key, Action<object[]> callback) { if (!_intReceivers.ContainsKey(key)) { _intReceivers[key] = callback; } _intReceivers[key] += callback; } public void RemoveListener(int key, Action<object[]> callback) { if (_intReceivers.ContainsKey(key)) { _intReceivers[key] -= callback; } } public void DispatchMsg(int key,params object[] args) { if (_intReceivers.ContainsKey(key)) _intReceivers[key]?.Invoke(args); }
public void AddListener(string key, Action<object[]> callback) { if (!_stringReceivers.ContainsKey(key)) { _stringReceivers[key] = callback; } _stringReceivers[key] += callback; }
public void RemoveListener(string key, Action<object[]> callback) { if (_stringReceivers.ContainsKey(key)) { _stringReceivers[key] -= callback; } }
public void DispatchMsg(string key, params object[] args) { if ( _stringReceivers.ContainsKey(key)) _stringReceivers[key]?.Invoke(args); } }
|
修改MessageMgr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| using System;
public class MessageMgr : NormalSingleton<MessageMgr>, IMessageSystem { private MessageSystem _messageSystem; public MessageMgr() { _messageSystem = new MessageSystem(); } public void AddListener(int key, Action<object[]> callback) { _messageSystem.AddListener(key, callback); }
public void AddListener(string key, Action<object[]> callback) { _messageSystem.AddListener(key, callback); }
public void DispatchMsg(int key, params object[] args) { _messageSystem.DispatchMsg(key, args); }
public void DispatchMsg(string key, params object[] args) { _messageSystem.DispatchMsg(key, args); }
public void RemoveListener(int key, Action<object[]> callback) { _messageSystem.RemoveListener(key, callback); }
public void RemoveListener(string key, Action<object[]> callback) { _messageSystem.RemoveListener(key, callback); } public void AddInputLintener(KeyCode code, InputState state, Action<object[]> callback) { var key = InputMgr.Instance.GetKey(code, state); AddListener(key, callback); } public void RemoveInputLintener(KeyCode code, InputState state, Action<object[]> callback) { var key = InputMgr.Instance.GetKey(code, state); RemoveListener(key, callback); } }
|
添加AddInputLintener
和RemoveInputLintener
,这两个是专门用来添加按键监听的API
删除IReceiver
删除掉Scripts——Module——Message文件夹下的IReceiver
接口
其他UI脚本修改
首先把这些报错的脚本的IReceiver
接口删掉
GameUIView
把里面的this
改为ReceiveMessage
方法即可,把ReceiveMessage
方法私有化
Life、LifeItem
把里面的this
改为ReceiveMessage
方法即可,把ReceiveMessage
方法私有化
Bomb
把里面的ReceiveMessage
方法切分为ReceiveBomb
和ReceiveHandState
方法,然后如下修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public override void Show() { base.Show(); MessageMgr.Instance.AddListener(MsgEvent.EVENT_BOMB, ReceiveBomb); MessageMgr.Instance.AddListener(MsgEvent.EVENT_USE_BOMB, ReceiveBomb); MessageMgr.Instance.AddListener(MsgEvent.EVENT_CHANGE_HAND, ReceiveHandState); _bombCount = GameModel.Instance.BombCount; UpdateCount(); UpdateState(); UpdateHandState(); } public override void Hide() { base.Hide(); MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_BOMB, ReceiveBomb); MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_USE_BOMB, ReceiveBomb); MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_CHANGE_HAND, ReceiveHandState); } private void ReceiveBomb(params object[] args) { UpdateCount(); UpdateState(); } private void ReceiveHandState(params object[] args) { UpdateHandState(); }
|
Shield
把里面的ReceiveMessage
方法切分为ReceiveShield
和ReceiveHandState
方法,然后如下修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public override void Show() { base.Show(); MessageMgr.Instance.AddListener(MsgEvent.EVENT_SHIELD, ReceiveShield); MessageMgr.Instance.AddListener(MsgEvent.EVENT_USE_SHIELD, ReceiveShield); MessageMgr.Instance.AddListener(MsgEvent.EVENT_CHANGE_HAND, ReceiveHandState); _shieldCount = GameModel.Instance.ShieldCount; UpdateCount(); UpdateState(); UpdateHandState(); } public override void Hide() { base.Hide(); MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_SHIELD, ReceiveShield); MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_USE_SHIELD,ReceiveShield); MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_CHANGE_HAND,ReceiveHandState); } public void ReceiveShield(params object[] args) { UpdateCount(); UpdateState(); } public void ReceiveHandState(params object[] args) { UpdateHandState(); }
|
ActionMgr
直接使用Action多播,会有重复监听的问题,我们实现一个ActionMgr
,使用HashSet<Action<T>>
来解决这个问题。
在Logic——Module文件夹内新建DelegateMgr文件夹,在其中新建ActionMgr
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| using System; using System.Collections.Generic;
public class ActionMgr {
}
public class ActionMgr<T> { private readonly HashSet<Action<T>> _actionHashSets; private Action<T> _action;
public ActionMgr() { _actionHashSets = new HashSet<Action<T>>(); _action = null; }
public void Add(Action<T> action) { if ( _actionHashSets.Add(action)) _action += action; } public void Remove(Action<T> action) { if ( _actionHashSets.Remove(action)) _action -= action; } public void Execute(T t) => _action?.Invoke(t); public bool Contains(Action<T> action) => _actionHashSets.Contains(action); }
|
使用HashSet<Action<T>>
来阻止监听相同的方法,使用Action<T>
来进行多播,我们不遍历HashSet来多播而是使用Action多播是因为方便判空。
修改MessageSystem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public class MessageSystem : IMessageSystem { private readonly Dictionary<int, ActionMgr<object[]>> _intReceivers = new(); private readonly Dictionary<string, ActionMgr<object[]>> _stringReceivers = new(); public void AddListener(int key, Action<object[]> callback) { if (!_intReceivers.ContainsKey(key)) { _intReceivers[key] = new ActionMgr<object[]>(); } _intReceivers[key].Add(callback); } public void RemoveListener(int key, Action<object[]> callback) { if (_intReceivers.ContainsKey(key)) { _intReceivers[key].Remove(callback); } } public void DispatchMsg(int key,params object[] args) { if (_intReceivers.ContainsKey(key)) _intReceivers[key].Execute(args); }
public void AddListener(string key, Action<object[]> callback) { if (!_stringReceivers.ContainsKey(key)) { _stringReceivers[key] = new ActionMgr<object[]>(); } _stringReceivers[key].Add(callback); }
public void RemoveListener(string key, Action<object[]> callback) { if (_stringReceivers.ContainsKey(key)) { _stringReceivers[key].Remove(callback); } }
public void DispatchMsg(string key, params object[] args) { if ( _stringReceivers.ContainsKey(key)) _stringReceivers[key].Execute(args); } }
|
子消息系统
消息系统仅支持大的消息,比如Player自己本身的消息,像敌机生命值变化这种消息就不适合了,因为我们不可能分配这么多消息的key,这个时候使用子消息系统。
在Manager文件夹中新建SubMsgMgr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| using System; using UnityEngine;
public class SubMsgMgr : MonoBehaviour, IMessageSystem { private readonly MessageSystem _messageSystem = new(); public void AddListener(int key, Action<object[]> callback) { _messageSystem.AddListener(key, callback); }
public void AddListener(string key, Action<object[]> callback) { _messageSystem.AddListener(key, callback); }
public void DispatchMsg(int key, params object[] args) { _messageSystem.DispatchMsg(key, args); }
public void DispatchMsg(string key, params object[] args) { _messageSystem.DispatchMsg(key, args); }
public void RemoveListener(int key, Action<object[]> callback) { _messageSystem.RemoveListener(key, callback); }
public void RemoveListener(string key, Action<object[]> callback) { _messageSystem.RemoveListener(key, callback); } }
|
子消息系统不使用单例,直接挂载在主GameObject上访问。
子消息系统完全可以使用和消息系统一样的ID,因为互相不是同一个实例,互不影响。
使用子消息系统会经常调用GetComponentInParent
方法,我们在GameUtil
中添加此方法
1
| public static SubMsgMgr GetSubMsgMgr(Transform trans) => trans.GetComponentInParent<SubMsgMgr>();
|