简易版GameFramework游戏框架搭建教程(六)Event

事件广播机制可以说是在各种框架里比较常见的解耦机制了,GF自然也不例外,在本篇文章中我们便来编写Event模块

下面是官网的介绍

 

在正式开始编写Event模块之前,我们需要完成一个前置的框架基础功能——ReferencePool(引用池),ReferencePool提供了一个IReference接口,只有实现了该接口的对象才会被纳入引用池管理当中,而在GF里,主要由Event模块的事件基类实现该接口,也就是说ReferencePool在框架里主要是负责管理Event模块的事件的引用的

 

首先在工程的Base文件夹下创建一个ReferencePool文件夹,新建IReference,ReferenceCollection和ReferencePool

其中

IReference上面已经说明过了

ReferenceCollection是引用集合类,负责管理实现了IReference的对象

ReferencePool是引用池类,负责管理所有的ReferenceCollection

 

打开IReference,将其修改为接口,并添加清理引用的方法

/// <summary>
/// 引用池对象接口
/// </summary>
public interface IReference{
    /// <summary>
    /// 清理引用
    /// </summary>
    void Clear();
}

接下来打开ReferenceCollection,为其添加字段与构造方法

/// <summary>
/// 引用集合
/// </summary>
public class ReferenceCollection{
    /// <summary>
    /// 引用队列
    /// </summary>
    private Queue<IReference> m_References;

    public ReferenceCollection()
    {
        m_References = new Queue<IReference>();
    }
}

并添加引用队列的相关方法

    /// <summary>
    /// 获取引用
    /// </summary>
    public T Acquire<T>() where T : class, IReference, new()
    {
        lock (m_References)
        {
            if (m_References.Count > 0)
            {
                return m_References.Dequeue() as T;
            }
        }

        return new T();
    } 

     /// <summary>
     /// 获取引用
     /// </summary>
     public IReference Acquire(Type referenceType)
     {
         lock (m_References)
         {
             if (m_References.Count > 0)
             {
                    return m_References.Dequeue();
             }
         }
         
         return (IReference)Activator.CreateInstance(referenceType);
     }

    /// <summary>
    /// 释放引用
    /// </summary>
    public void Release<T>(T reference) where T : class, IReference
    {
        reference.Clear();
        lock (m_References)
        {
            m_References.Enqueue(reference);
        }
    }

    /// <summary>
    /// 添加引用
    /// </summary>
    public void Add<T>(int count) where T : class, IReference, new()
    {
        lock (m_References)
        {
            while (count-- > 0)
            {
                m_References.Enqueue(new T());
            }
        }
    }

    /// <summary>
    /// 删除引用
    /// </summary>
    public void Remove<T>(int count) where T : class, IReference
    {
        lock (m_References)
        {
            if (count > m_References.Count)
            {
                count = m_References.Count;
            }

            while (count-- > 0)
            {
                m_References.Dequeue();
            }
        }
    }

    /// <summary>
    /// 删除所有引用
    /// </summary>
    public void RemoveAll()
    {
        lock (m_References)
        {
            m_References.Clear();
        }
    }

最后打开ReferencePool,将其修改为静态类,并为其添加字段与属性

/// <summary>
/// 引用池
/// </summary>
public static class ReferencePool{

    /// <summary>
    /// 引用集合的字典
    /// </summary>
    private static Dictionary<string, ReferenceCollection> s_ReferenceCollections = new Dictionary<string, ReferenceCollection>();

    /// <summary>
    /// 获取引用池的数量
    /// </summary>
    public static int Count
    {
        get
        {
            return s_ReferenceCollections.Count;
        }
    }

}

添加获取引用集合与清除所有引用集合的方法

    /// <summary>
    /// 获取引用集合
    /// </summary>
    private static ReferenceCollection GetReferenceCollection(string fullName)
    {
        ReferenceCollection referenceCollection = null;
        lock (s_ReferenceCollections)
        {
            if (!s_ReferenceCollections.TryGetValue(fullName, out referenceCollection))
            {
                referenceCollection = new ReferenceCollection();
                s_ReferenceCollections.Add(fullName, referenceCollection);
            }
        }

        return referenceCollection;
    }
    /// <summary>
    /// 清除所有引用集合
    /// </summary>
    public static void ClearAll()
    {
        lock (s_ReferenceCollections)
        {
            foreach (KeyValuePair<string, ReferenceCollection> referenceCollection in s_ReferenceCollections)
            {
                referenceCollection.Value.RemoveAll();
            }

            s_ReferenceCollections.Clear();
        }
    }

然后添加引用池的相关方法

追加与移除

    /// <summary>
    /// 向引用集合中追加指定数量的引用
    /// </summary>
    /// <typeparam name="T">引用类型</typeparam>
    /// <param name="count">追加数量</param>
    public static void Add<T>(int count) where T : class, IReference, new()
    {
        GetReferenceCollection(typeof(T).FullName).Add<T>(count);
    }

    /// <summary>
    /// 从引用集合中移除指定数量的引用
    /// </summary>
    /// <typeparam name="T">引用类型</typeparam>
    /// <param name="count">移除数量</param>
    public static void Remove<T>(int count) where T : class, IReference
    {
        GetReferenceCollection(typeof(T).FullName).Remove<T>(count);
    }

    /// <summary>
    /// 从引用集合中移除所有的引用
    /// </summary>
    /// <typeparam name="T">引用类型</typeparam>
    public static void RemoveAll<T>() where T : class, IReference
    {
        GetReferenceCollection(typeof(T).FullName).RemoveAll();
    }

获取与归还

    /// <summary>
    /// 从引用集合获取引用
    /// </summary>
    public static T Acquire<T>() where T : class, IReference, new()
    {
        return GetReferenceCollection(typeof(T).FullName).Acquire<T>();
    }

     /// <summary>
     /// 从引用池获取引用
     /// </summary>
     public static IReference Acquire(Type referenceType)
     {
         return GetReferenceCollection(referenceType.FullName).Acquire(referenceType);
     }

    /// <summary>
    /// 将引用归还引用集合
    /// </summary>
    /// <typeparam name="T">引用类型</typeparam>
    /// <param name="reference">引用</param>
    public static void Release<T>(T reference) where T : class, IReference
    {
        if (reference == null)
        {
            Debug.LogError("要归还的引用为空");
        }

        GetReferenceCollection(typeof(T).FullName).Release(reference);
    }

Ok,到这里ReferencePool功能编写完毕,可以正式开始Event模块的编写了

 

新建Event文件夹,在其中新建GlobalEventArgs,EventPool与EventManager

GlobalEventArgs是全局事件的基类,EventPool负责将某一类型的GlobalEventArgs封装后进行管理,EventManager负责管理所有的EventPool

首先打开GlobalEventArgs,将其修改为抽象类,继承EventArgs并实现IReference

/// <summary>
/// 全局事件基类(继承该类的事件类才能被事件池管理)
/// </summary>
public abstract class GlobalEventArgs : EventArgs,IReference {
    // <summary>
    /// 事件类型ID
    /// </summary>
    public abstract int Id { get; }

    public abstract void Clear();
}

然后打开EventPool,为其添加泛型与约束,并编写一个内部类Event封装GlobalEventArgs,用于进行线程安全的事件抛出

/// <summary>
/// 事件池
/// </summary>
public class EventPool<T> where T : GlobalEventArgs{

    /// <summary>
    /// 事件结点
    /// </summary>
    private class Event
    {

        public Event(object sender, T e)
        {
            Sender = sender;
            EventArgs = e;
        }
        /// <summary>
        /// 事件发送者
        /// </summary>
        public object Sender { get; private set; }

        /// <summary>
        /// 事件参数
        /// </summary>
        public T EventArgs { get; private set; }
    }

}

这里之所以要这么封装是因为事件处理方法的委托采用了C#的EventHandler,其形参为object与EvntArgs

接下来添加字段与属性,并在构造方法里进行初始化

    /// <summary>
    /// 事件码与对应处理方法的字典
    /// </summary>
    private Dictionary<int, EventHandler<T>> m_EventHandlers;

    /// <summary>
    /// 事件结点队列
    /// </summary>
    private Queue<Event> m_Events;

    public EventPool()
    {
        m_EventHandlers = new Dictionary<int, EventHandler<T>>();
        m_Events = new Queue<Event>();
    }

添加事件的订阅与取消的方法

    /// <summary>
    /// 检查订阅事件处理方法是否存在
    /// </summary>
    public bool Check(int id, EventHandler<T> handler)
    {
        if (handler == null)
        {
             Debug.LogError("事件处理方法为空");
             return false;
        }

        EventHandler<T> handlers = null;
        if (!m_EventHandlers.TryGetValue(id, out handlers))
        {
            return false;
        }

        if (handlers == null)
        {
            return false;
        }

        //遍历委托里的所有方法
        foreach (EventHandler<T> i in handlers.GetInvocationList())
        {
            if (i == handler)
            {
                return true;
            }
        }

        return false;
    }

    /// <summary>
    /// 订阅事件
    /// </summary>
    public void Subscribe(int id, EventHandler<T> handler)
    {
        if (handler == null)
        {
            Debug.LogError("事件处理方法为空,无法订阅");
            return;
        }

        EventHandler<T> eventHandler = null;
        //检查是否获取处理方法失败或获取到的为空
        if (!m_EventHandlers.TryGetValue(id, out eventHandler) || eventHandler == null)
        {
            m_EventHandlers[id] = handler;
        }
        //不为空,就检查是否处理方法重复了
        else if (Check(id, handler))
        {
            Debug.LogError("要订阅事件的处理方法已存在");
        }
        else
        {
            eventHandler += handler;
            m_EventHandlers[id] = eventHandler;
        }
    }

    /// <summary>
    /// 取消订阅事件
    /// </summary>
    public void Unsubscribe(int id, EventHandler<T> handler)
    {
        if (handler == null)
        {
            Debug.LogError("事件处理方法为空,无法取消订阅");
            return;
        }

        if (m_EventHandlers.ContainsKey(id))
        {
            m_EventHandlers[id] -= handler;
        }
    }

添加事件处理方法

    /// <summary>
    /// 处理事件
    /// </summary>
    /// <param name="sender">事件来源</param>
    /// <param name="e">事件参数</param>
    private void HandleEvent(object sender, T e)
    {
        //尝试获取事件的处理方法
        int eventId = e.Id;
        EventHandler<T> handlers = null;
        if (m_EventHandlers.TryGetValue(eventId, out handlers))
        {
            if (handlers != null)
            {
                handlers(sender, e);
            }
            else
            {
                Debug.LogError("事件没有对应处理方法:" + eventId);
            }        
        }
        
         //向引用池归还事件引用
         ReferencePool.Release(e);
    }

    /// <summary>
    /// 事件池轮询(用于处理线程安全的事件)
    /// </summary>
    public void Update(float elapseSeconds, float realElapseSeconds)
    {
        while (m_Events.Count > 0)
        {
            Event e = null;
            lock (m_Events)
            {
                e = m_Events.Dequeue();
            }
            //从封装的Event中取出事件数据进行处理
            HandleEvent(e.Sender, e.EventArgs);
        }
    }

添加事件抛出方法

    /// <summary>
    /// 抛出事件(线程安全)
    /// </summary>
    /// <param name="sender">事件源。</param>
    /// <param name="e">事件参数。</param>
    public void Fire(object sender, T e)
    {
        //将事件源和事件参数封装为Event加入队列
        Event eventNode = new Event(sender, e);
        lock (m_Events)
        {
            m_Events.Enqueue(eventNode);
        }
    }


    /// <summary>
    /// 抛出事件(线程不安全)
    /// </summary>
    /// <param name="sender">事件源</param>
    /// <param name="e">事件参数</param>
    public void FireNow(object sender, T e)
    {
        HandleEvent(sender, e);
    }

最后添加关闭与清理事件池的方法

    /// <summary>
    /// 清理事件。
    /// </summary>
    public void Clear()
    {
        lock (m_Events)
        {
            m_Events.Clear();
        }
    }

    /// <summary>
    /// 关闭并清理事件池。
    /// </summary>
    public void Shutdown()
    {
        Clear();
        m_EventHandlers.Clear();
    }

到这里EventPool的编写就完成了

接下来打开EventManager,继承ManagerBase,添加需要的字段与构造方法

/// <summary>
/// 事件管理器
/// </summary>
public class EventManager : ManagerBase
{
    /// <summary>
    /// 事件池
    /// </summary>
    private EventPool<GlobalEventArgs> m_EventPool;

    public override int Priority
    {
        get
        {
            return 100;
        }
    }

    public EventManager()
    {
        m_EventPool = new EventPool<GlobalEventArgs>();
    }

    public override void Init()
    {
        
    }

    public override void Shutdown()
    {
        //关闭并清理事件池
        m_EventPool.Shutdown();
    }

    /// <summary>
    /// 轮询事件池
    /// </summary>
    public override void Update(float elapseSeconds, float realElapseSeconds)
    {
        m_EventPool.Update(elapseSeconds, realElapseSeconds);
    }
}

添加事件的订阅与取消(其实就是对事件池的代理)

    /// <summary>
    /// 检查订阅事件处理方法是否存在
    /// </summary>
    public bool Check(int id, EventHandler<GlobalEventArgs> handler)
    {
        return m_EventPool.Check(id, handler);
    }

    /// <summary>
    /// 订阅事件
    /// </summary>
    public void Subscribe(int id, EventHandler<GlobalEventArgs> handler)
    {
        m_EventPool.Subscribe(id, handler);
    }

    /// <summary>
    /// 取消订阅事件
    /// </summary>
    public void Unsubscribe(int id, EventHandler<GlobalEventArgs> handler)
    {
        m_EventPool.Unsubscribe(id, handler);
    }

以及事件的抛出

    /// <summary>
    /// 抛出事件(线程安全)
    /// </summary>
    public void Fire(object sender, GlobalEventArgs e)
    {
        m_EventPool.Fire(sender, e);
    }

    /// <summary>
    /// 抛出事件(线程不安全)
    /// </summary>
    public void FireNow(object sender, GlobalEventArgs e)
    {
        m_EventPool.FireNow(sender, e);
    }

很简单的就完成了EventManager的编写,那么就轮到测试环节了

按照惯例,新建好文件夹,测试脚本,以及测试场景

打开EventTestArgs,使其继承GlobalEventArgs,并添加一个string字段

public class EventTestArgs : GlobalEventArgs
{

   public string m_Name;


    public override int Id
    {
        get
        {
            return 1;
        }
    }

    public override void Clear()
    {
        m_Name = string.Empty;
    }


    /// <summary>
    /// 事件填充
    /// </summary>
    public EventTestArgs Fill(string name)
    {
        m_Name = name;

        return this;
    }
}

打开测试脚本,进行事件的订阅与派发编写

public class EventTestMain : MonoBehaviour {


    private void Start()
    {
        //订阅事件
        FrameworkEntry.Instance.GetManager<EventManager>().Subscribe(1, EventTestMethod);
    }


    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
             EventTestArgs e = ReferencePool.Acquire<EventTestArgs>();
           
            //派发事件
            FrameworkEntry.Instance.GetManager<EventManager>().Fire(this, e.Fill("EventArgs"));
        }
    }


    /// <summary>
    /// 事件处理方法
    /// </summary>
    private void EventTestMethod(object sender, GlobalEventArgs e)
    {
        EventTestArgs args = e as EventTestArgs;
        Debug.Log(args.m_Name);
    }
}

然后启动游戏,点击左键,控制台成功输出了EventTest

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值