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);
    }
}