接觸Unity一段時間了,發現有時候處理角色某個屬性變化後須要更新到各個界面上會很麻煩,這時候用到事件通知就很方便,誰關心就訂閱這個事件。工具
這裏用一些類工具類:單例模板、自定義Event類測試
單例模板類,不少地方須要ui
namespace Util { public class Singleton<T> where T : Singleton<T>, new() { private static T mInstance; private static readonly object syslock = new object(); public static T GetInstance() { if (mInstance == null) { lock (syslock) { if (mInstance == null) { mInstance = new T(); } } } return mInstance; } } }
事件類this
namespace Util { public delegate void EventHandler<IEventArgs>(EVENT eventKey, IEventArgs e); class Event : Singleton<Event> { private readonly int mMaxKey = 1 << 31; private Dictionary<EVENT, Dictionary<EventKey, EventHandler<EventArgs>>> mDict = new Dictionary<EVENT, Dictionary<EventKey, EventHandler<EventArgs>>>(); private EventKey mKey = 0; //同一個事件可能被多個地方關心 則經過Key來區分 private EventKey GetKey() { mKey++; if (mKey == mMaxKey) { mKey = 0; } return mKey; } public EventKey AddListener(EVENT ev, EventHandler<EventArgs> handler) { if (!mDict.ContainsKey(ev)) { mDict[ev] = new Dictionary<uint, EventHandler<EventArgs>>(); } var key = GetKey(); mDict[ev].Add(key, handler); return key; } public void RemoveListener(EVENT ev, uint key) { if (mDict.ContainsKey(ev)) { mDict[ev].Remove(key); } } public void Notify(EVENT ev, EventArgs e) { if (mDict.ContainsKey(ev)) { ///防止事中執行 RemoveListener 導致迭代失效 Dictionary<EventKey, EventHandler<EventArgs>> eventTmp = new Dictionary<uint, EventHandler<EventArgs>>(); foreach (var kv in mDict[ev]) { eventTmp[kv.Key] = kv.Value; } foreach (var kv in eventTmp) { kv.Value(ev, e); } } } } }
事件枚舉以及參數lua
namespace Util { public enum EVENT { /// <summary> 測試 EventTestArgs </summary> EVENT_TEST = 1, } public class EventTestArgs : EventArgs { public int a { get; set; } public EventTestArgs(int a) { this.a = a; } } }
//發佈和訂閱事件 var key = Util.Event.GetInstance().AddListener(Util.EVENT.EVENT_TEST, (Util.EVENT eventId, System.EventArgs args) => { var testArgs = args as Util.EventTestArgs; }); Util.Event.GetInstance().Notify(Util.EVENT.EVENT_TEST, new Util.EventTestArgs(111));
其實想了想這樣使用參數優勢麻煩就是事件類繼承 System.EventArgs 類,那就須要事先申明,可能有些人會以爲很麻煩「好比,lua的話就很方便,傳遞任意table,這裏能夠使用可變參數」。可是想過沒這樣作是沒有規範化,後期很難維護,由於沒有約束全部多個地方發佈同一個事件可能會用了不一樣的參數,會出現莫名其妙的BUG。spa
優勢:blog
訂閱者不須要關係可變參數個數以及類型,方便後期維護繼承
缺點:事件
須要事先聲明ip
在事件通知在項目中具體使用則還須要數據類 Model,當數據改變的時候就發佈事件
//事件註冊封裝 ///全部UI的基類 Scene(場景) Panel(彈出面板,好比揹包) Tip(tips 好比點擊查看裝備) public class UIBase : MonoBehaviour { protected Dictionary<EventKey, Util.EVENT> mEventKeyDict = new Dictionary<EventKey, Util.EVENT>(); ... //UI銷燬調用 protected virtual void OnDestroyBegin() { foreach (var it in mEventKeyDict) { Util.Event.GetInstance().RemoveListener(it.Value, it.Key); } } /// <summary> /// 監聽 Event 事件,界面銷燬會自動移除全部事件 /// </summary> /// <param name="ev"></param> /// <param name="handler"></param> /// <returns></returns> protected EventKey AddEventListener(Util.EVENT ev, Util.EventHandler<EventArgs> handler) { var key = Util.Event.GetInstance().AddListener(ev, handler); mEventKeyDict[key] = ev; return key; } /// <summary> /// 移除監聽 Event 事件 /// </summary> /// <param name="ev"></param> /// <param name="key"></param> protected void RemoveEventListener(Util.EVENT ev, EventKey key) { mEventKeyDict.Remove(key); Util.Event.GetInstance().RemoveListener(ev, key); } }
//數據Model基類 public class ModelBase { } //用戶數據類 public class UserModel : ModelBase { #region 用戶ID private uint userId; public uint UserId { set { userId = value; Util.Event.GetInstance().Notify(Util.EVENT.EVENT_CHANGE_NAME, new Util.EventChangeUserIdArgs(userId)); } get { return userId; } } #endregion }
//Model管理類 public class ModelMgr : Util.Singleton<ModelMgr> { private Dictionary<string, ModelBase> mMondeDict = new Dictionary<string, ModelBase>(); public T GetModel<T>() where T:ModelBase { Type type = typeof(T); T model; string name = type.Name; if (!mMondeDict.ContainsKey(name)) { model = System.Activator.CreateInstance(type) as T; mMondeDict[name] = model; } else { model = mMondeDict[name] as T; } return model; } }
#region 事件使用 AddEventListener(Util.EVENT.EVENT_TEST, (Util.EVENT eventId, System.EventArgs args) => { ModelMgr.GetInstance().GetModel<UserModel>().UserId = 123456; }); #endregion
只要接收到Test事件就將 UserModel 的 UserId改變,UserModel再將UserId發佈出去,誰關心這個UserId就監聽 EVENT_CHANGE_NAME 事件,收到後再渲染到界面上