Unity 基于UnityEvent实现轻量级事件管理器

本文参考:https://blog.csdn.net/z625309640/article/details/80547877

代码在文末,直接复制到你的工程中,就可以使用

 

目录

一、UnityEvent

二、本事件系统主要原理

三、单例模式

四、正式开始写代码吧

五、怎么使用?

六、完整代码

七、装箱拆箱优化


因为这个简单的事件管理器在我完成某项作业时帮了我很大的忙,所以特地记录下;

该事件系统是基于UnityEvent实现的,在应对小型工程时还是游刃有余的,可以说是解耦利器,如果你是unity初学者的话,可以试一试这个事件系统。原博客只给出了代码,我这边就稍微扩展一下

一、UnityEvent

 UnityEvent有三个公共方法,官网截图如下:

Unity 基于UnityEvent实现轻量级事件管理器_第1张图片

 分别是:

将监听函数添加UnityEvent;

触发调用已注册的监听函数(回调函数)

删除监听

在代码量较少的情况下,直接使用是没有什么问题,但是一旦代码数量变大,工程中的模块变多,就会显得很混乱,这个时候就有必要对其进行封装

二、本事件系统主要原理

这个事件系统主要通过一个字典将抽象的事件和具体的回调函数联系在一起,我们可以这样定义这个字典:

private Dictionary eventDictionary = new Dictionary();

对于字典的值:如果UnityEvent想要带有参数,可以在上面代码的基础上,将UnityEvent改为UnityEvent,由于事先不知道传的参数类型,可以直接定义为object类型,即UnityEvent

或者还想加入其他的一些操作,可以创建一个新类,继承至UnityEvent,可以将上面的代码改为:

        private class EventMode:UnityEvent
        {

        }
        private Dictionary m_eventDictionary = new Dictionary(); 
  

对于字典的键:定义为System.Enum,它是一个抽象类,在c#中,所有的枚举类都继承自它。因此,可以在一个文件中专门定义不同操作的枚举类,在本文后面的实例演示中,就可看到用这个抽象类做字典的键的好处,最终可以实现枚举类中的某一个值与其对应的事件进行绑定,并存放在字典中,这样的功能;

三、单例模式

在正式通过代码实现这个事件系统时,我们还要考虑,对于这个事件系统,我们是不是只希望在整个游戏中只有一个实例,并持久化的存在,如果是的话,我们可以使用单例模式:

        /// 
        /// 单例模式
        /// 
        private static LiteEventManager instance;

        public static LiteEventManager Instance
        {
            get
            {
                if(instance == null)
                {
                    instance = new LiteEventManager();
                }
                return instance;
            }
        }

四、正式开始写代码吧

到目前为止,我们已经写了一部分代码了,接下来将要把事件系统的三个主要功能对应的函数实现了,分别是添加监听者,触发事件,删除事件;

4.1、添加监听者(Register)

        /// 
        /// 添加监听者
        /// 
        /// 
        /// 
        public void Register(Enum eventKey,UnityAction listener)
        {
            EventMode tempEvent = null;
            if(m_eventDictionary.TryGetValue(eventKey,out tempEvent))
            {
                tempEvent.AddListener(listener);
            }
            else
            {
                tempEvent = new EventMode();
                tempEvent.AddListener(listener);
                m_eventDictionary.Add(eventKey, tempEvent);
            }
        } 
  

第一部分,首先检查字典中是否已经存在将要注册的键值对,如果存在,就直接获取对应的UnityEvent,并调用AddListener函数添加监听;

第二部分,如果字典中不存在键值对,则对应响应的操作;

4.2、删除监听(RemoveListering)

删除监听非常重要,如果你在一个自定义类中使用了事件系统,在销毁这个自定义类的时候记得删除对应的监听,不然会出现很多意外情况,例如c#会自动回收内存,当你的类在整个工程中都没有被使用时,才会真正的销毁你这个类;如果没有删除监听,由于事件系统是持久化存在的,那这个类就会一直存在,这样就很麻烦了。废话不多说,删除监听的代码如下:

        public void RemoveListering(Enum eventKey,UnityAction listener)
        {
            if (instance == null) return;
            EventMode tempEvent = null;
            if(m_eventDictionary.TryGetValue(eventKey,out tempEvent))
            {
                tempEvent.RemoveListener(listener);
            }
        } 
  

4.3、触发监听函数(TriggerEvent)

代码如下:
 

        public void TriggerEvent(Enum eventKey,object trigger)
        {
            EventMode tempEvent;

            if(m_eventDictionary.TryGetValue(eventKey,out tempEvent))
            {
                //Debug.Log("触发前:" + eventKey);
                //var enumra = m_eventDictionary.GetEnumerator();
                //while(enumra.MoveNext())
                //{
                //    Debug.Log(enumra.Current.Key + " " + enumra.Current.Value.GetHashCode());
                //}
                
                tempEvent.Invoke(trigger);
                //Debug.Log("触发后:" + eventKey);
            }
        }

五、怎么使用?

使用起来很简单,首先定义一个枚举类,其次在需要监听事件的模块中添加监听,接着在需要触发的模块中触发事件就可以了

列举一个实例如下:

    
//定义一个控制主角信号的枚举类
    //主角数值变化的key
    public enum AvatarValueKey
    {
        GunValue,
        BulletsCount,
        GunChange,
        Shoot,
        Reload,
        Launch,
        ShootStop,
        ShootNoGun
    }
//添加事件监听
LiteEventManager.Instance.Register(AvatarValueKey.GunValue, AttackValueChange);

其中MoveAhead为事件响应函数,其定义形式如下:

        public void AttackValueChange(object obj)
        {
            int temp = (int)obj;
            Debug.Log("攻击力:" + temp);
        }

对于object,需要装箱拆箱操作,也就是强制类型转换

在该要触发事件的地方通过以下代码触发事件:

//触发事件
LiteEventManager.Instance.Register(AvatarValueKey.GunValue, 10);

在对象销毁时解除事件绑定

//解除事件绑定
LiteEventManager.Instance.RemoveListering(AvatarValueKey.GunValue, AttackValueChange);

至此,整个使用流程展示完毕;

六、完整代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using System;

namespace EventSys
{

    public class LiteEventManager 
    {
        private class EventMode:UnityEvent
        {

        }
        private Dictionary m_eventDictionary = new Dictionary();
        /// 
        /// 单例模式
        /// 
        #region
        private static LiteEventManager instance;

        public static LiteEventManager Instance
        {
            get
            {
                if(instance == null)
                {
                    instance = new LiteEventManager();
                }
                return instance;
            }
        }
        #endregion
        /// 
        /// 添加监听者
        /// 
        /// 
        /// 
        public void Register(Enum eventKey,UnityAction listener)
        {
            EventMode tempEvent = null;
            if(m_eventDictionary.TryGetValue(eventKey,out tempEvent))
            {
                tempEvent.AddListener(listener);
            }
            else
            {
                tempEvent = new EventMode();
                tempEvent.AddListener(listener);
                m_eventDictionary.Add(eventKey, tempEvent);
            }
        }

        public void RemoveListering(Enum eventKey,UnityAction listener)
        {
            if (instance == null) return;
            EventMode tempEvent = null;
            if(m_eventDictionary.TryGetValue(eventKey,out tempEvent))
            {
                tempEvent.RemoveListener(listener);
            }
        }

        public void TriggerEvent(Enum eventKey,object trigger)
        {
            EventMode tempEvent;

            if(m_eventDictionary.TryGetValue(eventKey,out tempEvent))
            {
                //Debug.Log("触发前:" + eventKey);
                //var enumra = m_eventDictionary.GetEnumerator();
                //while(enumra.MoveNext())
                //{
                //    Debug.Log(enumra.Current.Key + " " + enumra.Current.Value.GetHashCode());
                //}
                
                tempEvent.Invoke(trigger);
                //Debug.Log("触发后:" + eventKey);
            }
        }
    }
}

 
  

七、装箱拆箱优化

可以将object类替换为自定义类,通过模板和多态实现多参数的传递

    public class SendBaseClass
    {

    }
    public class ValueInfo : SendBaseClass
    {
        public T1 param_1;
        public T2 param_2;
        public T3 param_3;
        public T4 param_4;
        public ValueInfo(T1 par, T2 par2, T3 par3,T4 par4)
        {
            param_1 = par;
            param_2 = par2;
            param_3 = par3;
            param_4 = par4;
        }
        public ValueInfo()
        {

        }

    }

    public class ValueInfo : SendBaseClass
    {
        public T1 param_1;
        public T2 param_2;
        public T3 param_3;
        public T4 param_4;
        public T5 param_5;
        public ValueInfo(T1 par, T2 par2, T3 par3, T4 par4, T5 par5)
        {
            param_1 = par;
            param_2 = par2;
            param_3 = par3;
            param_4 = par4;
            param_5 = par5;
        }
        public ValueInfo()
        {

        }

    }
    //三参数
    public class ValueInfo:SendBaseClass
    {
        public T1 param_1;
        public T2 param_2;
        public T3 param_3;
        public ValueInfo(T1 par,T2 par2,T3 par3)
        {
            param_1 = par;
            param_2 = par2;
            param_3 = par3;
        }
        public ValueInfo()
        {

        }

    }
    //两参数
    public class ValueInfo : SendBaseClass
    {
        public T1 param_1;
        public T2 param_2;
        public ValueInfo(T1 par, T2 par2)
        {
            param_1 = par;
            param_2 = par2;
        }
        public ValueInfo()
        {

        }
    }

 

你可能感兴趣的:(Unity,轻量级游戏框架,Unity,TPS游戏设计)