跳至正文

事件中心

观察者模式

大概理解就是一种一对多的关系

当这个特定对象的状态发生改变的时候

所有依赖于这个对象的对象们都会收到改变的信息

然后做出对应的反应

事件中心是什么

我们知道框架里可能有很多个不同的管理器

但是这些管理器肯定没办法做到完全独立

彼此之间还是会有一些逻辑处理和数据交换

同理 游戏里有很多不同的对象 对象之间也涉及到交流

假如互相引用的话代码看起来就不太好看

所以我们就想到了交给第三方来处理

也就是事件中心

作为发起人 我告诉事件中心什么事情发生了

作为委托人 我告诉事件中心当某件事发生时我会去做什么

然后事件中心就开始监听 监听到发起人说某件事发生了

便会告诉委托人你现在该做啥了

这就是事件中心的工作原理

PS:这里的坑在于监听对象是否有限制的问题

事件中心的代码实现

我们依然选择了字典作为我们的存放对象

string作为key 也就是事件名

而事件作为我们的值

关于这里事件的封装其实比较没有那么复杂

其实就是包装了一层接口 然后做里氏替换

关键是为什么要这样做呢?

事件中心这个类是单例对吧 他是唯一的 那么给事件中心这个类加上泛型就会很奇怪

因为它只会初始化一次 你只能传一次类型

例如说你想在类里直接用UnityAction<T> 哎 你发现你还得给单例加上泛型

所以这里我们在外面小小封装了一下

不用object很明显 就是为了避免装箱拆箱

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

public interface I_EventGeneric
{ 
    
}

public class EventGeneric<T> : I_EventGeneric
{
    public UnityAction<T> actions;

    public EventGeneric(UnityAction<T> action)
    {
        actions += action;
    }
}

public class EventGeneric : I_EventGeneric
{
    public UnityAction actions;

    public EventGeneric(UnityAction action)
    {
        actions += action;
    }
}

/// <summary>
/// 事件中心 单例
/// 事件触发要晚于事件监听
/// </summary>
public class EventCenter : BaseManager<EventCenter>
{
    //存放事件的容器 Key是事件名 value是监听事件对应的委托函数们
    private Dictionary<string, I_EventGeneric> eventDic = new Dictionary<string, I_EventGeneric>();

    /// <summary>
    /// 添加事件监听
    /// </summary>
    /// <param name="name">事件名</param>
    /// <param name="action">处理事件的委托函数</param>
    public void AddEventListener<T>(string name, UnityAction<T> action)
    {
        //有没有对应的事件监听
        if(eventDic.ContainsKey(name))
        {
            //有就直接添加
            (eventDic[name] as EventGeneric<T>).actions += action;
        }
        else
        {
            //没有就要创建
            eventDic.Add(name, new EventGeneric<T>(action));
        }
    }

    /// <summary>
    /// 移除事件监听
    /// </summary>
    /// <param name="name">移除的事件名</param>
    /// <param name="action">事件添加的委托函数</param>
    public void RemoveEventListener<T>(string name, UnityAction<T> action)
    {
        if (eventDic.ContainsKey(name))
        {
            (eventDic[name] as EventGeneric<T>).actions -= action;
        }
        //销毁时调用 OnDestroy()
    }

    /// <summary>
    /// 事件触发
    /// </summary>
    /// <param name="name">触发的事件名</param>
    public void EventTrigger<T>(string name, T info)
    {
        if (eventDic.ContainsKey(name) && (eventDic[name] as EventGeneric<T>).actions != null) 
        {
            (eventDic[name] as EventGeneric<T>).actions.Invoke(info);
        }
    }

    /// <summary>
    /// 清空事件中心 防止场景切换时溢出
    /// </summary>
    public void Clear()
    {
        eventDic.Clear();
    }
}

事件中心的使用

假设现在我们有一个怪物类 作为事件的发起者 死亡的时候进行触发

有一个任务类和一个玩家类 分别委托了怪物死亡的时候要做的事情

具体实现如下

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

public class Task : MonoBehaviour
{
    void Awake()
    {
        EventCenter.GetInstance().AddEventListener<Monster>("MonsterDie", TaskDone);       
    }

    void OnDestroy()
    {
        EventCenter.GetInstance().RemoveEventListener<Monster>("MonsterDie", TaskDone);
    }

    public void TaskDone(Monster info)
    {
        Debug.Log("任务完成");
    }
}

 


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

public class Monster : MonoBehaviour
{

    void Start()
    {
        Die();
    }

    public void Die()
    {
        Debug.Log("怪物死亡");
        //触发事件
        EventCenter.GetInstance().EventTrigger("MonsterDie", this);
    }

}

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

public class Player : MonoBehaviour
{
    // Start is called before the first frame update
    void Awake()
    {
        EventCenter.GetInstance().AddEventListener<Monster>("MonsterDie", GetBonus);        
    }

    void OnDestroy()
    {
        EventCenter.GetInstance().RemoveEventListener<Monster>("MonsterDie", GetBonus);
    }

    public void GetBonus(Monster info)
    {
        Debug.Log(info.gameObject.name +"死了,增加金币");
    }    
}

 

发表评论

您的电子邮箱地址不会被公开。