Unity常用设计模式-观察者模式:游戏中的事件传播
·
一、简介
当在Unity中应用观察者模式时,通常是为了实现一种解耦和灵活性较强的事件机制。观察者模式允许一个对象(称为主题)维护一组依赖于它的对象(称为观察者),当主题的状态发生变化时,所有观察者都能够得到通知并进行相应的处理。
二、观察者模式的优势
-
松耦合:
观察者模式通过将主题和观察者解耦,使得它们之间的依赖关系更加松散。主题并不需要知道观察者的具体类别,只需知道它们实现了相同的接口或继承了相同的基类。这使得系统更容易维护和扩展。 -
可维护性和可扩展性:
由于主题和观察者之间的解耦,系统更容易维护。在不修改主题的情况下,可以轻松地添加或删除观察者,从而扩展系统的功能。 -
事件驱动的设计:
观察者模式天然支持事件驱动的设计。主题的状态变化会触发事件,通知所有观察者进行相应的处理。这在Unity中特别有用,例如在游戏中处理用户输入、游戏状态变化等。 -
多播通知:
观察者模式允许一个主题同时通知多个观察者。这意味着可以有多个对象独立地响应主题的变化,而不需要主题知道观察者的具体数量或类型。 -
解耦 UI 和数据:
在游戏开发中,观察者模式经常用于解耦UI和底层数据。当数据发生变化时,通过观察者模式通知UI进行更新,使得UI可以保持与数据的同步。 -
易于理解和维护:
观察者模式是一种经典的设计模式,因此它的使用方式是广泛接受的。这使得代码更易于理解,也更容易维护,因为开发人员可以更容易地理解和预测代码的行为。
总体而言,观察者模式在Unity中的应用能够提高代码的灵活性、可维护性和可扩展性,是一种强大的设计模式。
三、观察者模式的具体适用案例
观察者模式在Unity当中的适用场景有很多,下面主要介绍的是事件管理器的应用,直接上代码。
using Mr.Le.Utility.Singleton;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace Mr.Le.Utility.Manager
{
/// <summary>
/// 事件管理器
/// </summary>
public class EventCenterManager : NoMonoSingleton<EventCenterManager>
{
/// <summary>
/// 用于里氏替换的接口
/// </summary>
private interface IEventInfo
{
}
//事件集合
private Dictionary<string, IEventInfo> _events = new Dictionary<string, IEventInfo>();
#region 无参事件
private class EventInfo : IEventInfo
{
public UnityAction action;
/// <summary>
/// 构造函数添加事件
/// </summary>
/// <param name="call"></param>
public EventInfo(UnityAction call)
{
action += call;
}
}
/// <summary>
/// 添加事件监听
/// </summary>
/// <param name="command"></param>
/// <param name="call"></param>
public void AddEventListener(object command, UnityAction call)
{
string name = command.GetType().Name + "_" + command;
if (_events.ContainsKey(name))
{
(_events[name] as EventInfo).action += call;
}
else
{
_events.Add(name, new EventInfo(call));
}
}
/// <summary>
/// 移除指定事件的指定监听
/// </summary>
/// <param name="command"></param>
/// <param name="call"></param>
public void RemoveEventListener(object command, UnityAction call)
{
string name = command.GetType().Name + "_" + command;
if (_events.ContainsKey(name))
{
(_events[name] as EventInfo).action -= call;
}
else
{
Debug.LogError($"{name}事件不存在!");
return;
}
}
/// <summary>
/// 移除指定事件的所有监听
/// </summary>
/// <param name="command"></param>
public void RemoveAllEventListeners(object command)
{
string name = command.GetType().Name + "_" + command;
if (_events.ContainsKey(name))
{
(_events[name] as EventInfo).action = null;
}
else
{
Debug.LogError($"{name}事件不存在!");
return;
}
}
/// <summary>
/// 呼叫事件
/// </summary>
/// <param name="command"></param>
public void CallEvent(object command)
{
string name = command.GetType().Name + "_" + command;
if (_events.ContainsKey(name))
{
(_events[name] as EventInfo).action?.Invoke();
}
else
{
Debug.LogError($"{name}事件不存在!");
return;
}
}
#endregion
#region 有参事件
private class EventInfo<T> : IEventInfo
{
public UnityAction<T> action;
/// <summary>
/// 构造函数添加事件
/// </summary>
/// <param name="call"></param>
public EventInfo(UnityAction<T> call)
{
action += call;
}
}
/// <summary>
/// 添加事件监听
/// </summary>
/// <param name="command"></param>
/// <param name="call"></param>
public void AddEventListener<T>(object command, UnityAction<T> call)
{
string name = command.GetType().Name + "_" + command;
if (_events.ContainsKey(name))
{
(_events[name] as EventInfo<T>).action += call;
}
else
{
_events.Add(name, new EventInfo<T>(call));
}
}
/// <summary>
/// 移除指定事件的指定监听
/// </summary>
/// <param name="command"></param>
/// <param name="call"></param>
public void RemoveEventListener<T>(object command, UnityAction<T> call)
{
string name = command.GetType().Name + "_" + command;
if (_events.ContainsKey(name))
{
(_events[name] as EventInfo<T>).action -= call;
}
else
{
Debug.LogError($"{name}事件不存在!");
return;
}
}
/// <summary>
/// 移除指定事件的所有监听
/// </summary>
/// <param name="command"></param>
public void RemoveAllEventListeners<T>(object command)
{
string name = command.GetType().Name + "_" + command;
if (_events.ContainsKey(name))
{
(_events[name] as EventInfo<T>).action = null;
}
else
{
Debug.LogError($"{name}事件不存在!");
return;
}
}
/// <summary>
/// 呼叫事件
/// </summary>
/// <param name="command"></param>
public void CallEvent<T>(object command, T value)
{
string name = command.GetType().Name + "_" + command;
if (_events.ContainsKey(name))
{
(_events[name] as EventInfo<T>).action?.Invoke(value);
}
else
{
Debug.LogError($"{name}事件不存在!");
return;
}
}
#endregion
}
}
测试代码:
无参事件:
被观察类:
public class Observer : MonoBehaviour
{
private void OnEnable()
{
EventCenterManager.Instance.AddEventListener("事件1", TestFunc);
}
private void OnDisable()
{
EventCenterManager.Instance.RemoveEventListener("事件1", TestFunc);
}
private void TestFunc()
{
Debug.Log("这是一个无参事件的测试方法");
}
}
调用事件:
public class Player : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
EventCenterManager.Instance.CallEvent("事件1");
}
// Update is called once per frame
void Update()
{
}
}

有参事件:
被观察者类
public class Observer : MonoBehaviour
{
private void OnEnable()
{
EventCenterManager.Instance.AddEventListener<bool>("事件2", TestFunc);
}
private void OnDisable()
{
EventCenterManager.Instance.RemoveEventListener<bool>("事件2", TestFunc);
}
private void TestFunc(bool value)
{
if (value)
Debug.Log("这是一个真消息!");
else
Debug.Log("这是一个假消息!");
}
}
调用事件:
public class Player : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
EventCenterManager.Instance.CallEvent("事件2",true);
}
// Update is called once per frame
void Update()
{
}
}

通过以上示例,你可以更好地理解在Unity中如何使用观察者模式来实现一种灵活且可维护的设计。
希望这篇文章对你理解和应用观察者模式有所帮助。如果有任何疑问或建议,请在评论区留言。
更多推荐


所有评论(0)