Unity游戏框架学习笔记——03基于观察者模式的事件中心

Unity游戏框架学习笔记——03基于观察者模式的事件中心

基于观察者模式的事件中心

一如既往指路牌:https://www.bilibili.com/video/BV1C441117wU?p=5.
视频原本是免费的,完整视频移步泰课吧/(ㄒoㄒ)/~~…
泰课指路牌:https://www.taikr.com/course/1062/task/31006/show.

引入

对于一般写脚本的话,如果要实现监听某个状态,让脚本在该状态下实现相应动作的执行,都是在更新这个状态对应的脚本里,判断该状态为真后,获取其余对象中关心该状态的脚本里方法来执行。

举个栗子:

怪物的身上挂着一个Monster脚本,其中有一个方法MonsterDead();
然后
玩家关心奖励,玩家身上有一个Player脚本,里面有一个PlayerWaitMonsterDead()方法会在怪物死亡执行一些操作;
任务系统会更新,任务系统身上也有一个Task脚本,里面有TaskWaitMonsterDead()方法是关心怪物死亡的;
其他的一些同样关心怪物死亡的脚本也是一样有相关方法;

那么
当怪物死亡时,如果要通知这些脚本执行关心怪物死亡的方法的话,我们一般也是在怪物脚本的MonsterDead()方法里面,获取相关的脚本,执行相应方法:
MonsterDead()
{
// Monster Dead //
GameObject.Find(“Player”).GetComponent< Player >().PlayerWaitMonsterDead();
GameObject.Find(“Task”).GetComponent< Task >().TaskWaitMonsterDead();
GameObject.Find(“Other”).GetComponent< Other >().OtherWaitMonsterDead();
}

可以看出,在Monster脚本里,不可避免地出现了很多其他脚本的方法
那么我们不妨夸张的想一想,当一个游戏很大,里面的功能也更加多的时候,上述这样的情况是不是就会出现得更加频繁,脚本和脚本之间联系也会乱七八糟,这个时候脚本之间就出现所说的耦合性较大的情况,而且脚本之间也会很复杂
如果在这个时候,要额外增加一些功能的话,就会牵一发而动全身了,大改自然是必须的了。
于是为了降低程序之间的耦合性,较小复杂性,提高开发效率,基于观察者模式的事件中心,也就应运而生了。

事件中心

事件中心就是字面上的意思,具体实现的功能就是负责事件的记录和分发。
比如说当怪物死亡,那么怪物就向事件中心公布派发出“怪物死亡了”这个事件,事件中心接收到之后,就会提醒订阅了该事件的方法,让关心这个事件的方法执行。

代码

/// <summary>
/// 事件中心类
/// </summary>
public class EventCenter : Singleton<EventCenter>
{
    // key —— 时间名字
    // value —— 关心这个事件的委托方法们
    public Dictionary<string, UnityAction> eventsDictonary = new Dictionary<string, UnityAction>();

    /// <summary>
    /// 给关心这个事件的方法提供监听(订阅)
    /// </summary>
    /// <param name="eventName"></param>
    /// <param name="action"></param>
    public void EventAddListener(string eventName, UnityAction action)
    {
        // 存在存在要监听的事件
        if (eventsDictonary.ContainsKey(eventName))
        {
            eventsDictonary[eventName]+= action;
        }
        // 要监听的事件不存在,添加对应事件和订阅后执行的方法
        else
        {
            eventsDictonary.Add(eventName,action);
        }
    }

    /// <summary>
    /// 事件触发
    /// </summary>
    /// <param name="EventName">触发的事件名字</param>
    public void EventTrigger(string eventName)
    {
        // 存在存在要监听的事件,依次执行订阅了的委托方法
        if (eventsDictonary.ContainsKey(eventName))
        {
            // eventsDictonary[eventName]();
            eventsDictonary[eventName].Invoke();
        }
    }
}
使用

在关心这个事件的对象的脚本中,
在对象生成时,即在Start()函数体中,添加订阅即可。

那么这个时候就会存在一个问题:
挂载该脚本的对象一旦被销毁,但事件中心中仍然保持着这个对象的方法对事件的监听,会导致内存泄露,因为对于这个事件监听的引用依然存在,所以,在添加了监听之后,在对象的销毁时,要对监听事件经行移除。

    /// <summary>
    /// 移除事件监听
    /// </summary>
    /// <param name="eventName">监听的事件名称</param>
    /// <param name="action">监听事件的委托方法</param>
    public void RemoveListener(string eventName, UnityAction action)
    {
        if (eventsDictonary.ContainsKey(eventName))
            eventsDictonary[eventName] -= action;
    }

那么就需要在对象销毁时,在OnDestory()中调用该方法,将订阅的事件移除。

功能完善


由于事件中心类的单例特性,在内存中是静态区域的,即在程序一开始的时候分配内存空间,直至程序结束时才会释放,那么在场景切换时,下一个场景的内存中还会保留着上一个场景的事件订阅,虽然有对象销毁时的事件监听移除,但为以防万一,还是把事件中心里面的事件监听给移除要好一点。

    /// <summary>
    /// 清楚所有事件以及监听关系
    /// 主要用在场景切换时
    /// </summary>
    public void ClearEvent()
    {
        eventsDictonary.Clear();
    }


解决监听方法执行的信息传递,将委托方法改为Object类型的参数

/// <summary>
/// 事件中心类
/// </summary>
public class EventCenter : Singleton<EventCenter>
{
    // key —— 时间名字
    // value —— 关心这个事件的委托方法们
    public Dictionary<string, UnityAction<object>> eventsDictonary = new Dictionary<string, UnityAction<object>>();

    /// <summary>
    /// 给关心这个事件的方法提供监听(订阅)
    /// </summary>
    /// <param name="eventName"></param>
    /// <param name="action"></param>
    public void EventAddListener(string eventName, UnityAction<object> action)
    {
        // 存在存在要监听的事件
        if (eventsDictonary.ContainsKey(eventName))
        {
            eventsDictonary[eventName]+= action;
        }
        // 要监听的事件不存在,添加对应事件和订阅后执行的方法
        else
        {
            eventsDictonary.Add(eventName,action);
        }
    }

    /// <summary>
    /// 事件触发
    /// </summary>
    /// <param name="eventName">触发事件的名字</param>
    /// <param name="info">传递的参数消息</param>
    public void EventTrigger(string eventName, object info)
    {
        // 存在存在要监听的事件,依次执行订阅了的委托方法
        if (eventsDictonary.ContainsKey(eventName))
        {
            // eventsDictonary[eventName](info);
            eventsDictonary[eventName].Invoke(info);
        }
    }

    /// <summary>
    /// 移除事件监听
    /// </summary>
    /// <param name="eventName">监听的事件名称</param>
    /// <param name="action">监听事件的委托方法</param>
    public void RemoveListener(string eventName, UnityAction<object> action)
    {
        if (eventsDictonary.ContainsKey(eventName))
            eventsDictonary[eventName] -= action;
    }

    /// <summary>
    /// 清楚所有事件以及监听关系
    /// 主要用在场景切换时
    /// </summary>
    public void ClearEvent()
    {
        eventsDictonary.Clear();
    }    
}

关于 Object 和 object

object 是泛类, 引用的是System.Object.

Object 引用的是UnityEngine.Object,是Unity所有组件Component和GameObject的父类;

UnityEngine.Object继承System.Object.

原创文章 7 获赞 6 访问量 935

猜你喜欢

转载自blog.csdn.net/LAUGINJI/article/details/105923749