Unity3D设计模式之观察者模式(16)(一)

xiaoxiao2021-02-28  98

首先这里要提到一种称为“观察者模式”的设计模式,这种设计模式在《大话设计模式》这本书中称为“观察者模式”或者“发布-订阅(Publish/Subscribe)模式”,我们这里暂且叫做“观察者模式”吧!该模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个对象在状态发生变化时会通知所有观察者对象,使它们能够自动更新自己。针对这个模式,我们可以考虑事件机制的实现,事件机制可以理解为在一个事件中心(Subject)保存有对所有事件(Observer)的引用,事件中心负责对这些事件进行分发,这样每个事件就可以通过回调函数的方式进行更新,这样就实现了一个事件机制。 

观察者模式定义了一种一对多的依赖关系,让多个观察者同时监听一个对象,这个对象在状态发生变化的时候,通知所有观察者对象。

下面给出基本的代码实现:

using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace UniEventDispatcher { /// <summary> /// 定义事件分发委托 /// </summary> public delegate void OnNotification(Notification notific); /// <summary> ///通知中心 /// </summary> public class NotificationCenter { /// <summary> /// 通知中心单例 /// </summary> private static NotificationCenter instance=null; public static NotificationCenter Get() { if(instance == null){ instance = new NotificationCenter(); return instance; } return instance; } /// <summary> /// 存储事件的字典 /// </summary> private Dictionary<string,OnNotification> eventListeners = new Dictionary<string, OnNotification>(); /// <summary> /// 注册事件 /// </summary> /// <param name="eventKey">事件Key</param> /// <param name="eventListener">事件监听器</param> public void AddEventListener(string eventKey,OnNotification eventListener) { if(!eventListeners.ContainsKey(eventKey)){ eventListeners.Add(eventKey,eventListener); } } /// <summary> /// 移除事件 /// </summary> /// <param name="eventKey">事件Key</param> public void RemoveEventListener(string eventKey) { if(!eventListeners.ContainsKey(eventKey)) return; eventListeners[eventKey] =null; eventListeners.Remove(eventKey); } /// <summary> /// 分发事件 /// </summary> /// <param name="eventKey">事件Key</param> /// <param name="notific">通知</param> public void DispatchEvent(string eventKey,Notification notific) { if (!eventListeners.ContainsKey(eventKey)) return; eventListeners[eventKey](notific); } /// <summary> /// 分发事件 /// </summary> /// <param name="eventKey">事件Key</param> /// <param name="sender">发送者</param> /// <param name="param">通知内容</param> public void DispatchEvent(string eventKey, GameObject sender, object param) { if(!eventListeners.ContainsKey(eventKey)) return; eventListeners[eventKey](new Notification(sender,param)); } /// <summary> /// 分发事件 /// </summary> /// <param name="eventKey">事件Key</param> /// <param name="param">通知内容</param> public void DispatchEvent(string eventKey,object param) { if(!eventListeners.ContainsKey(eventKey)) return; eventListeners[eventKey](new Notification(param)); } /// <summary> /// 是否存在指定事件的监听器 /// </summary> public Boolean HasEventListener(string eventKey) { return eventListeners.ContainsKey(eventKey); } } }

注意到在这个“通知中心”中,我们首先实现了单例模式,这样我们可以通过Get方法来获取该“通知中心”的唯一实例,其次这里利用一个字典来存储对所有事件的引用,这样保证外部可以通过AddEventListener和RemoveEventListener这两个方法来进行事件的添加和移除,对于添加的事件引用我们可以通过DispatchEvent方法来分发一个事件,事件的回调函数采用委托来实现,注意到这个委托需要一个Notification类型,对该类型简单定义如下:

using System; using UnityEngine; namespace UniEventDispatcher { public class Notification { /// <summary> /// 通知发送者 /// </summary> public GameObject sender; /// <summary> /// 通知内容 /// 备注:在发送消息时需要装箱、解析消息时需要拆箱 /// 所以这是一个糟糕的设计,需要注意。 /// </summary> public object param; /// <summary> /// 构造函数 /// </summary> /// <param name="sender">通知发送者</param> /// <param name="param">通知内容</param> public Notification(GameObject sender, object param) { this.sender = sender; this.param = param; } /// <summary> /// 构造函数 /// </summary> /// <param name="param"></param> public Notification(object param) { this.sender = null; this.param = param; } /// <summary> /// 实现ToString方法 /// </summary> /// <returns></returns> public override string ToString() { return string.Format("sender={0},param={1}", this.sender, this.param); } } }

使用事件机制的一个示例

这里以一个简单的示例来验证事件机制的可行性,我们在场景中有一个球体,默认这个球体的颜色为白色,通过调整界面中的RGB数值,可以改变球体的颜色,在这个示例中UI是事件发送者,负责UI中Slider控件的数值发生变化时向球体发送消息,传递的数据类型是Color类型;球体为事件接收者,负责注册事件及接收到消息后的处理。因为代码较为简单,所以这里写在一个脚本中:

using UnityEngine; using UnityEngine.UI; using System.Collections; using UniEventDispatcher; public class Example : MonoBehaviour { /// <summary> /// R数值的Slider /// </summary> private Slider sliderR; /// <summary> /// G数值的Slider /// </summary> private Slider sliderG; /// <summary> /// B数值的Slider /// </summary> private Slider sliderB; private  Button bu; Slider sliderh; void Start () { //在接收者中注册事件及其回调方法 NotificationCenter.Get().AddEventListener("ChangeColor", ChangeColor); //在发送者中分发事件,这里以UI逻辑为例 sliderR = GameObject.Find("Canvas/SliderR").GetComponent<Slider>(); sliderG = GameObject.Find("Canvas/SliderG").GetComponent<Slider>(); sliderB = GameObject.Find("Canvas/SliderB").GetComponent<Slider>(); //注册UI事件 sliderR.onValueChanged.AddListener(OnValueChanged); sliderG.onValueChanged.AddListener(OnValueChanged); sliderB.onValueChanged.AddListener(OnValueChanged); } public void OnValueChanged(float value) { //获得RGB数值 float r = sliderR.value; float g = sliderG.value; float b = sliderB.value; //分发事件,注意和接收者协议一致 NotificationCenter.Get().DispatchEvent("ChangeColor", new Color(r, g, b)); } /// <summary> /// 改变物体材质颜色 /// </summary> /// <param name="notific"></param> public void ChangeColor(Notification notific) { Debug.Log(notific.ToString()); //设置颜色 this.transform.GetComponent<Renderer>().material.color = (Color)notific.param; } }

该示例运行效果如下:

项目工程地址:

http://download.csdn.net/detail/u011480667/9922790

实用型的案例见下:

转载请注明原文地址: https://www.6miu.com/read-51824.html

最新回复(0)