Bootstrap

Unity 设计模式-观察者模式(Observer Pattern)详解

观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系。当一个对象的状态发生变化时,它的所有依赖者(观察者)都会收到通知并自动更新。这种模式用于事件处理系统,当某个对象发生改变时,依赖该对象的其他对象能够及时响应。

观察者模式中,主要有两个角色:

  1. 被观察者(Subject):状态发生变化的对象,它维护了一组观察者列表,并在状态发生变化时通知它们。
  2. 观察者(Observer):依赖被观察者的对象,它注册到被观察者上,并在被观察者状态变化时得到通知。

1、什么时候使用观察者模式

事件系统:需要实现基于事件驱动的系统,许多UI系统使用观察者模式来创建按钮点击,值变化等事件

数据同步:当某个对象的状态改变时,依赖它的其他对象需要同步更新。

系统中存在一对多的依赖关系:例如,一个对象的变化需要通知多个对象做出反应,典型的例子包括股票价格变化通知、新闻订阅系统等。

通知机制:在某些情况下,多个模块需要接收到某个变化的信息(如游戏事件、社交媒体通知等)。

2、使用观察者模式的好处

解耦合:观察者和被观察者之间的耦合度低。被观察者只知道观察者实现了某些接口,而无需了解它们的具体实现细节。
灵活性:可以动态添加或移除观察者,系统的扩展性好。
提高代码的可维护性:将状态的变化与相应的反应分开,使代码更易于维护和修改。
响应式更新:观察者模式允许系统中的多个对象自动响应某个对象的状态变化,无需显式调用每个依赖对象。

3、使用时的注意事项

性能问题:当观察者数量过多时,每次状态改变都需要通知所有观察者,这可能会引起性能问题。
避免循环依赖:如果观察者在更新过程中再次触发了被观察者的通知,可能会导致循环调用或死锁。
顺序问题:多个观察者对同一事件做出响应时,要注意观察者之间的顺序依赖,可能会导致某些观察者未按预期更新。
内存泄漏问题:要确保观察者可以正确地从被观察者中移除,以避免内存泄漏问题。

4、我现在用通俗的例子来给大家描述一下观察者模式

想象有一个人气明星,比如周杰伦~。杰伦有非常多的歌迷,这些歌迷对他的新专辑翘首以盼(话说距离上次伟作已经过去两年了……),都在关注着杰伦的专辑动向,一有点风吹草动大家就会沸腾。那么,观察杰伦的歌迷们就是【观察者】,被歌迷们观察的杰伦就是【被观察者】。

在观察者模式中,被观察者的状态发生改变时,就会向所有的观察者们发送通知,观察者们就可以根据这个通知做出各自相应的行为。类比到杰伦和歌迷上,就是当杰伦发新专辑时(简直天方夜谭!),他会在各种社交媒体、音乐软件上发布这个消息,而所有关注杰伦的歌迷在看到这一消息后,有的掏出钱包,有的奔走相告,有的因激动而变身狒狒:吼吼哇哇!

当然,观察者模式中还有一个很重要的概念:【主题】,我更习惯称呼它为【中间体】。中间体是观察者和被观察者之间的桥梁,就好像一个代理人,负责管理有哪些观察者正在观察自己代理的被观察者。每当被观察者状态改变发送消息时,消息首先到达中间体,再由中间体传递出去。在杰伦和歌迷的比喻中,中间体就好比是社交媒体、音乐平台。

在 Unity 中使用 观察者模式

为了演示如何在 Unity 中使用 观察者模式。

我们将实现一个示例:当玩家接触到一个触发器(Trigger)时,游戏会通知观察者更新,例如改变颜色、显示文本等。

这个示例将演示如何使用观察者模式管理多个对象对玩家触发事件做出反应。

1、定义观察者接口

首先,我们定义一个观察者接口,所有的观察者类都需要实现这个接口。

public interface IObserver
{
    void OnNotify();
}
2、定义被观察者类

然后我们定义一个被观察者类。在这个示例中,被观察者是一个触发器,当玩家接触触发器时,它会通知所有观察者。

using System.Collections.Generic;
using UnityEngine;
 
public class TriggerSubject : MonoBehaviour
{
    private List<IObserver> observers = new List<IObserver>();
 
    // 注册观察者
    public void RegisterObserver(IObserver observer)
    {
        observers.Add(observer);
    }
 
    // 移除观察者
    public void RemoveObserver(IObserver observer)
    {
        observers.Remove(observer);
    }
 
    // 通知所有观察者
    public void NotifyObservers()
    {
        foreach (IObserver observer in observers)
        {
            observer.OnNotify();
        }
    }
 
    // Unity 的触发器事件,当玩家接触触发器时调用
    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            NotifyObservers();
        }
    }
}
3、实现观察者类

接下来我们创建几个不同的观察者类,每个观察者类会响应触发器的通知。在这个例子中,我们将创建两个观察者:

  • 一个会改变颜色
  • 一个会显示文本

3.1 观察者 1:改变颜色的观察者

using UnityEngine;
 
public class ColorObserver : MonoBehaviour, IObserver
{
    public Renderer objectRenderer;
 
    public void OnNotify()
    {
        // 随机改变对象的颜色
        objectRenderer.material.color = new Color(Random.value, Random.value, Random.value);
        Debug.Log("ColorObserver: Color changed!");
    }
}

3.2 观察者 2:显示文本的观察者

using UnityEngine;
using UnityEngine.UI;
 
public class TextObserver : MonoBehaviour, IObserver
{
    public Text messageText;
 
    public void OnNotify()
    {
        // 显示通知文本
        messageText.text = "Player triggered the event!";
        Debug.Log("TextObserver: Text updated!");
    }
}
4、在场景中使用观察者模式

现在我们在 Unity 场景中设置以下内容:

  1. 创建一个空的 GameObject,命名为 Trigger,并将 TriggerSubject 脚本附加到该对象上。同时,在该对象上添加一个 BoxCollider,并勾选 Is Trigger。
  2. 创建两个 3D 物体(如立方体或球体),并附加 ColorObserver 脚本到其中一个物体,记得将 objectRenderer 变量拖入到 Inspector 中。
  3. 创建一个 UI 文本,附加 TextObserver 脚本,并将 messageText 变量拖入 Inspector。
  4. 在游戏的 Start() 函数中,注册观察者。
using UnityEngine;
 
public class GameManager : MonoBehaviour
{
    public TriggerSubject triggerSubject;
    public ColorObserver colorObserver;
    public TextObserver textObserver;
 
    void Start()
    {
        // 注册观察者
        triggerSubject.RegisterObserver(colorObserver);
        triggerSubject.RegisterObserver(textObserver);
    }
 
    void OnDestroy()
    {
        // 在销毁时移除观察者,避免内存泄漏
        triggerSubject.RemoveObserver(colorObserver);
        triggerSubject.RemoveObserver(textObserver);
    }
}
5、运行示例

1.将 GameManager 脚本挂载到 Unity 场景中的一个空对象上。
2.在 GameManager 中的 Inspector 窗口,将 triggerSubject、colorObserver 和 textObserver 分别拖入相应的字段。
3.运行游戏,当玩家接触 Trigger 对象时,注册的观察者将会被通知:
   物体的颜色会发生变化。
   UI 文本会显示 "Player triggered the event!"。

6、示例分析
  • 触发器 (TriggerSubject) 是被观察者,当玩家接触它时,它会调用 NotifyObservers() 方法通知所有的观察者。
  • 观察者 (ColorObserver 和 TextObserver) 实现了 IObserver 接口,并在 OnNotify() 方法中定义了各自的行为。
  • 通过这种设计,任何新增的观察者只需要实现 IObserver 接口,并注册到 TriggerSubject 中,而不需要修改已有的代码。

通过这个示例,我们可以看到如何在 Unity 中运用 观察者模式,处理多个对象对同一事件的响应。这种模式在处理游戏事件、状态变化等场景中十分有用,尤其是在复杂的游戏系统中。

今天是2024年12月3日

重复一段毒鸡汤来勉励我和你

你的对手在看书

你的仇人在磨刀

你的闺蜜在减肥

隔壁的老王在练腰

而你在干嘛?

;