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日

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

你的对手在看书

你的仇人在磨刀

你的闺蜜在减肥

隔壁的老王在练腰

而你在干嘛?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/485594.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第四篇:k8s 理解Service工作原理

什么是service&#xff1f; Service是将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法。 简单来说K8s提供了service对象来访问pod。我们在《k8s网络模型与集群通信》中也说过k8s集群中的每一个Pod&#xff08;最小调度单位&#xff09;都有自己的IP地址&#xff0c;都…

地瓜RDK X5上手ollama大模型测试

地瓜RDK X5上手ollama大模型测试 契机 ⚙ 上次逛ollama的时候发现有很多小参数的大模型&#xff0c;比如qwen2:0.5b,llama3.2:1b&#xff0c;甚至还有一个1.8b的多模态模型moondream&#xff0c;找公司1拿到一块RDK X5的开发板&#xff0c;官网查看算力可达10TOPS&#xff0c…

【Java】反射简介

框架的核心和架构师的核心 反射和代理是重中之重 反射 反射的作用 在运行的时候由代码获取类的信息 三种获取类信息的方式&#xff1a; 对象.getClass()Class.forName("类的路径")类.class Class &#xff1a;一个用来存储类信息的类 获取类信息是获取的整体的…

Windows电脑伪关机(快速启动模式),怎么真关机

Windows电脑在关机的时候&#xff0c;进入到一个伪关机的状态&#xff0c;也就是并没有真正的关机&#xff0c;但是在一些系统更新、变更了一些设置&#xff0c;进行重启等操作也会进入到真关机状态 这种一般是开启快速启动模式&#xff0c;开启了快速启动模式功能会在关机的时…

在c#控制台中使用Raylib-cs库,绘制控制小球和插入音频(附带c++中小球的控制代码)

下载网址 GitHub - chrisdill/raylib-cs: C# bindings for raylib, a simple and easy-to-use library to learn videogames programming 克隆库 克隆GitHub仓库-CSDN博客 1 .制作dll 点击 生成之后就会多出这些东西 2.在项目中添加dll 然后就导进来了 测试一下用例代码 …

【开源免费】基于Vue和SpringBoot的服装生产管理系统(附论文)

博主说明&#xff1a;本文项目编号 T 066 &#xff0c;文末自助获取源码 \color{red}{T066&#xff0c;文末自助获取源码} T066&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

【漫话机器学习系列】Adaboost算法

Adaboost&#xff08;Adaptive Boosting&#xff09;是一种经典的集成学习方法&#xff0c;主要思想是通过将多个弱学习器&#xff08;通常是简单模型&#xff0c;如决策树桩&#xff09;加权组合&#xff0c;来提升整体模型的预测能力。Adaboost 是一种自适应的学习方法&#…

WebStorm快捷键保持跟Idea一致

修改连续行局部多选 在WebStorm中同时按下ctrl alt s&#xff1b; 选择KeyMap 输入Column Selection Mode选择快捷键, 右键选择Add Mouse Shortcut 按下alt 鼠标左键 如果出现占用的情况&#xff0c;直接删除其他使用该快捷键的地方即可&#xff1b; 修改跨行局部多选 在…

图的遍历之DFS邻接矩阵法

本题要求实现一个函数&#xff0c;对给定的用邻接矩阵存储的无向无权图&#xff0c;以及一个顶点的编号v&#xff0c;打印以v为起点的一个深度优先搜索序列。 当搜索路径不唯一时&#xff0c;总是选取编号较小的邻接点。 本题保证输入的数据&#xff08;顶点数量、起点的编号等…

如何解决java.lang.UnsatisfiedLinkError:org.hyperic.sigar.ProcStat.gather问题

在新装的centos7.4服务器上部署部署应用系统&#xff0c;应用系统系统启动报错&#xff1a;“java.lang.UnsatisfiedLinkError:org.hyperic.sigar.ProcStat.gather” 一、报错分析 java.lang.UnsatisfiedLinkError通常是由于Java程序无法找到、加载或链接到所需的本地库而引发的…

Qt Chart 模块化封装曲线图

一 版本说明 此文档会从头到尾演示创建初始化流程 二 完成示例 此文章包含:曲线轴设置,曲线切换,单条曲线显示,坐标轴。。。 三 曲线图UI创建 在UI界面拖放一个QWidget,然后在 Widget里面放一个 graphicsView 四 代码介绍 1 头文件 #include <QString> #includ…

【时时三省】(C语言基础)结构体内存对齐

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 我们已经掌握了结构体的基本使用了。现在我们深入讨论一个问题&#xff1a;计算结构体的大小。 这也是一个特别热门的考点&#xff1a;结构体内存对齐 示例&#xff1a; 第一个s如果根据字…

云数据库 MongoDB

MongoDB 是一个基于文档的 NoSQL 数据库&#xff0c;它与传统的关系型数据库不同&#xff0c;采用的是灵活的文档结构&#xff08;类似 JSON 格式&#xff09;。MongoDB 是开源的&#xff0c;且高度可扩展&#xff0c;通常用于处理大量的非结构化或半结构化数据。 云数据库 Mon…

第一个 JSP 程序

一个简单的 JSP 程序&#xff1a; 使用 IDEA 开发工具新建一个 maven 项目&#xff0c;具体操作如图所示&#xff1a; 配置 Tomcat 服务器 项目结构如下图所示&#xff1a; 3. 修改 index.jsp 页面的代码&#xff1a; <% page language"java" contentType&q…

项目代码第2讲:从0实现LoginController.cs,UsersController.cs、User相关的后端接口对应的前端界面

一、User 1、使用数据注解设置主键和外键 设置主键&#xff1a;在User类的U_uid属性上使用[Key]注解。 设置外键&#xff1a;在Order类中&#xff0c;创建一个表示外键的属性&#xff08;例如UserU_uid&#xff09;&#xff0c;并使用[ForeignKey]注解指定它引用User类的哪个…

【NLP 9、实践 ① 五维随机向量交叉熵多分类】

目录 五维向量交叉熵多分类 规律&#xff1a; 实现&#xff1a; 1.设计模型 2.生成数据集 3.模型测试 4.模型训练 5.对训练的模型进行验证 调用模型 你的平静&#xff0c;是你最强的力量 —— 24.12.6 五维向量交叉熵多分类 规律&#xff1a; x是一个五维(索引)向量&#xff…

01-Chromedriver下载与配置(mac)

下载地址&#xff1a; 这里我用的最后一个&#xff0c;根据自己chrome浏览器选择相应的版本号即可 ChromeDriver官网下载地址&#xff1a;https://sites.google.com/chromium.org/driver/downloads ChromeDriver官网最新版下载地址&#xff1a;https://googlechromelabs.git…

Redis(上)

Redis 基础 什么是 Redis&#xff1f; Redis &#xff08;REmote DIctionary Server&#xff09;是一个基于 C 语言开发的开源 NoSQL 数据库&#xff08;BSD 许可&#xff09;。与传统数据库不同的是&#xff0c;Redis 的数据是保存在内存中的&#xff08;内存数据库&#xf…

JS学习(1)(基本概念与作用、与HTML、CSS区别)

目录 一、JavaScript是什么&#xff1f; &#xff08;1&#xff09;基本介绍 &#xff08;2&#xff09;简称&#xff1a;JS&#xff1f; 二、JavaScript的作用。 三、HTML、CSS、JS之间的关系。 &#xff08;1&#xff09;html、css。 &#xff08;2&#xff09;JavaScript。 …

使用AI工具Screenshot to Code将UI设计图翻译成代码

一、获取openAI apikey。 一般有两种方式&#xff0c;一种是到openAI官网注册账号&#xff0c;付费申请GPT4的apikey。另一种是某宝买代理。我这里采用第二种。 二、安装Screenshot to Code 1.到github下载源码。 2.启动&#xff0c;两种方式&#xff1a;源码启动和docker启动…