接觸Unity一段時間了,發現有時候處理角色某個屬性變化后需要更新到各個界面上會很麻煩,這時候用到事件通知就很方便,誰關心就訂閱這個事件。
這里用一些類工具類:單例模板、自定義Event類
單例模板類,很多地方需要
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;
}
}
}
事件類
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);
}
}
}
}
}
事件枚舉以及參數
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。
優點:
訂閱者不需要關系可變參數個數以及類型,方便后期維護
缺點:
需要事先聲明
在事件通知在項目中具體使用則還需要數據類 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 事件,收到后再渲染到界面上
