unity中应该至少有2类Event系统。一种是UnityEvent,这个用起来很方便,只要在脚本里定义了一个UnityEvent,就可以在面板通过拖拉的方式设置消息的响应者。但问题是如果是不能在面板拖来拖去的场合,就有点繁琐。而且假如有一大波新对象需要接收老信息,这个维护起来很是很繁琐的。还有一种是利用了Eventsystem的消息系统,但这个系统与ui系统整合,虽然仍然可以自定义消息,但是只要是同一个物体上挂接了多个同类消息的触发器,就会被全部执行这一点,在某些时候是很不方便的(比方说我们希望消息按照确定的响应顺序执行)为此,我决定写一个自己的消息系统,实现消息的自动注册,并且可以通过参数来控制响应顺序。只要物体上挂接了对应的sensor(listener),不管trigger在什么位置,都可以接收到对应的消息。这样整个消息系统里面有新的对象加入,都没有必要去维护老的代码。
首先我们定义一个Event基类,因为不直接使用本类所以定义为abstract,并且提供一个区分事件ID接口来让子类实现。
//事件基类 派生的事件可以包含更多的数据结构
//标识为abstract 防止直接使用该类
public abstract class Event
{
abstract public int EventID
{
get;
}
GameObject _sender;
public GameObject Sender
{
get { return _sender; }
set { _sender = value; }
}
GameObject _target;
public GameObject Target
{
get { return _target; }
set { _target = value; }
}
//广播 标识该事件如何发送以及是否可以中途中断传递
bool _need_broadCast = true;
public bool BroadCarst
{
get { return _need_broadCast; }
set { _need_broadCast = value; }
}
//重置属性 可覆写该函数以初始化自定义的事件数据
virtual public void reset()
{
_sender = null;
_target = null;
_need_broadCast = true;
}
}
接下来是感知器。为了让管理中心能够管理所有的感知器,先设计一个基础的接口,继承compare接口是为了实现优先权的排序。
public interface BaseSensor : IComparable<BaseSensor>
{
//虚基础属性接口
int Priority { get; set; }
int EventID { get; }
GameObject gb { get; }
bool EnableSensor { get; set; }
//消息处理接口
void OnEvent<T>(T t) where T:Event;
}
所有可用感知器应从一个模版派生。在start函数中调用EventCenter注册自身,这样我们就不用自己去研究接口的注册。
public abstract class Sensor<T> : MonoBehaviour, BaseSensor where T : Event, new()
{
virtual public void Start()
{
EventCenter.Instance.RigisterSensor<T>(this);
}
/BaseSensor interface imp
//是否激活
bool _enableSensor = true;
public bool EnableSensor
{
get { return _enableSensor; }
set { _enableSensor = value; }
}
int priority = 0;
public int Priority
{
get { return priority; }
set { priority = value; }
}
int _eventid = 0;
public int EventID
{
get
{
if (_eventid == 0)
{
T t = new T();
_eventid = t.EventID;
}
return _eventid;
}
}
GameObject BaseSensor.gb
{
get
{
return gameObject;
}
}
///
public int CompareTo(BaseSensor other) { return other.Priority - priority; }
/// ///
abstract public void OnEvent<T>(T t) where T : Event;
}
接下来是EventCenter的代码。使用字典及list来保存注册的感知器。使用sort保证按我们想要的顺序执行事件的处理。
//事件中心
public class EventCenter : Singleton<EventCenter>
{
Dictionary<int, List<BaseSensor>> _eventMap = new Dictionary<int, List<BaseSensor>>();
//注册 如果已经注册返回FALSE
public bool RigisterSensor<T>(Sensor<T> s) where T : Event, new()
{
if (s)
{
if (_eventMap.ContainsKey(s.EventID))
{
List<BaseSensor> bs = _eventMap[s.EventID];
if (bs.Contains(s))
return false;
else
{
bs.Add(s);
bs.Sort();
Debug.Log("add list " + bs.Count + " priority is " + s.Priority);
return true;
}
}
else
{
List<BaseSensor> bs = new List<BaseSensor>();
_eventMap.Add(s.EventID, bs);
Debug.Log("add map " + s.EventID);
bs.Add(s);
bs.Sort();
Debug.Log("add list " + bs.Count + " priority is " + s.Priority);
return true;
}
}
return false;
}
//反注册 如果没有注册返回false
public bool UnRigisterSensor<T>(Sensor<T> s) where T : Event, new()
{
if (s)
{
if (_eventMap.ContainsKey(s.EventID))
{
List<BaseSensor> bs = _eventMap[s.EventID];
return bs.Remove(s);
}
}
return false;
}
public void OnEvent<T>(T t) where T : Event
{
Debug.Log("on event " + t.EventID);
if (_eventMap.ContainsKey(t.EventID))
{
Debug.Log("has eventid");
List<BaseSensor> bs = _eventMap[t.EventID];
foreach (var item in bs)
{
Debug.Log("deal sensor" + item.gb.GetHashCode() + " enablesensor " + item.EnableSensor);
if (t.BroadCarst && item.EnableSensor)
{
item.OnEvent<T>(t);
}
}
}
}
~EventCenter()
{
_eventMap = null;
}
}
触发器很简单。只需要脚本中调用EventCenter的onEvent即可。这里提供一个可以每帧自动循环检查触发条件的trigger基类。这个可以根据自身的需要写不同的触发器。
//帧循环触发器基类
public abstract class Trigger<T> : MonoBehaviour where T : Event, new()
{
//保存一个事件信息的结构 用于发送
private T t = new T();
protected T EventData
{
get
{
t.reset();
t.Sender = gameObject;
return t;
}
}
//检查触发条件(每帧循环)
abstract protected bool Check();
protected void fire()
{
EventCenter.Instance.OnEvent<T>(t);
}
// Update is called once per frame
void Update()
{
//use monobehaviour enable
if (enabled)
{
if (Check())
fire();
}
}
}
这样整个自身的EventSystem就搭建完毕。使用时,应先定义一个自己的事件:
public class TestEvent : GD3.Event
{
public override int EventID
{
get
{
return 1;
}
}
}
然后使用该Event派生对应的Trigger及Sensor.
public class TestTrigger : Trigger<TestEvent> {
int i = 120;
protected override bool Check()
{
i = i - 1;
if (i <= 0)
{
Debug.Log("checked trigger happen");
i = 120;
TestEvent t = EventData;
t.BroadCarst = true;
return true;
}
else
return false;
}
}
public class TestSensor : GD3.Sensor<TestEvent> {
static int p = 5;
public override void Start()
{
this.Priority = p;
if (p == 6)
{
Debug.Log("p == 6");
EnableSensor = false;
}
p = p + 1;
base.Start();
}
public override void OnEvent<TestEvent>(TestEvent t)
{
Debug.Log("event on " + this.GetHashCode() + " p is " + Priority);
}
}
最后将trigger及sensor脚本挂接到具体的物体上即可。只要写好了这2个脚本,随便挂接任何物体,都可以接收到对应的事件消息。
另附一个EventCenter使用的比较简单的单例模式。需要注意的是这个单例模版不能应用到任何一个unity的类型上面,只能是自己写的类,继承UNITY的也不行。
public class Singleton<T> where T: new()
{
static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = new T();
}
return instance;
}
}
public void destroy()
{
instance = default(T);
}
}