接觸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 事件,收到後再渲染到界面上