【行为型之观察者模式】游戏开发实战——Unity事件驱动架构的核心实现策略

news2025/5/16 1:22:10

文章目录

      • 🎯 观察者模式(Observer Pattern)深度解析
        • 一、模式本质与核心价值
        • 二、经典UML结构
        • 三、Unity实战代码(玩家血量监控系统)
          • 1. 定义观察者接口与主题基类
          • 2. 实现具体主题(玩家血量)
          • 3. 实现具体观察者
          • 4. 客户端使用
        • 四、模式进阶技巧
          • 1. 事件总线系统
          • 2. 条件过滤器
          • 3. 异步事件处理
        • 五、游戏开发典型应用场景
        • 六、性能优化策略
        • 七、模式对比与选择
        • 八、最佳实践原则
        • 九、常见问题解决方案

🎯 观察者模式(Observer Pattern)深度解析

——以Unity实现动态事件通知跨系统响应为核心案例


一、模式本质与核心价值

核心目标
建立对象间的一对多依赖,实现状态变化自动通知
解耦主题与观察者,提升系统扩展性与维护性
✅ 支持动态订阅机制,灵活管理观察关系

关键术语

  • Subject(主题):状态变化的对象(如玩家血量)
  • Observer(观察者):监听状态变化的对象(如UI、成就系统)
  • Notification(通知):主题向观察者传递的信息

数学表达
设主题S有观察者集合O={o₁, o₂, …, oₙ},当S变化时:
S.Update() → ∀o ∈ O, o.OnNotify()


二、经典UML结构
«interface»
ISubject
+RegisterObserver(IObserver)
+RemoveObserver(IObserver)
+NotifyObservers()
«interface»
IObserver
+OnNotify()
PlayerHealth
-observers: List<IObserver>
+TakeDamage()
+Heal()
HealthUI
+OnNotify()
AchievementSystem
+OnNotify()

三、Unity实战代码(玩家血量监控系统)
1. 定义观察者接口与主题基类
public interface IObserver {
    void OnNotify(HealthData data);
}

public interface ISubject {
    void RegisterObserver(IObserver observer);
    void RemoveObserver(IObserver observer);
    void NotifyObservers();
}

[System.Serializable]
public struct HealthData {
    public float CurrentHealth;
    public float MaxHealth;
    public float DamageAmount;
}
2. 实现具体主题(玩家血量)
public class PlayerHealth : MonoBehaviour, ISubject {
    [SerializeField] private float maxHealth = 100f;
    private float _currentHealth;
    private List<IObserver> _observers = new();

    void Start() {
        _currentHealth = maxHealth;
    }

    public void TakeDamage(float damage) {
        _currentHealth = Mathf.Max(_currentHealth - damage, 0);
        NotifyObservers(new HealthData {
            CurrentHealth = _currentHealth,
            MaxHealth = maxHealth,
            DamageAmount = damage
        });
        if(_currentHealth <= 0) Die();
    }

    public void RegisterObserver(IObserver observer) => _observers.Add(observer);
    public void RemoveObserver(IObserver observer) => _observers.Remove(observer);
    
    private void NotifyObservers(HealthData data) {
        foreach(var observer in _observers) {
            observer.OnNotify(data);
        }
    }
}
3. 实现具体观察者
// 血条UI
public class HealthBarUI : MonoBehaviour, IObserver {
    [SerializeField] private Image healthFill;
    
    public void OnNotify(HealthData data) {
        healthFill.fillAmount = data.CurrentHealth / data.MaxHealth;
        Debug.Log($"血条更新:{healthFill.fillAmount:P0}");
    }
}

// 成就系统
public class AchievementTracker : MonoBehaviour, IObserver {
    private int _consecutiveHits;
    
    public void OnNotify(HealthData data) {
        if(data.DamageAmount > 0) {
            _consecutiveHits++;
            if(_consecutiveHits >= 3) {
                Debug.Log("解锁成就:连续受伤三次!");
            }
        } else {
            _consecutiveHits = 0;
        }
    }
}
4. 客户端使用
public class GameManager : MonoBehaviour {
    [SerializeField] private PlayerHealth playerHealth;
    [SerializeField] private HealthBarUI healthUI;
    [SerializeField] private AchievementTracker achievementTracker;

    void Start() {
        playerHealth.RegisterObserver(healthUI);
        playerHealth.RegisterObserver(achievementTracker);
    }

    void Update() {
        if(Input.GetKeyDown(KeyCode.Space)) {
            playerHealth.TakeDamage(10);
        }
    }
}

四、模式进阶技巧
1. 事件总线系统
public static class EventBus {
    private static Dictionary<Type, List<Action<object>>> _handlers = new();

    public static void Subscribe<T>(Action<T> handler) {
        Type type = typeof(T);
        if(!_handlers.ContainsKey(type)) _handlers[type] = new List<Action<object>>();
        _handlers[type].Add(obj => handler((T)obj));
    }

    public static void Publish<T>(T eventData) {
        Type type = typeof(T);
        if(_handlers.TryGetValue(type, out var handlers)) {
            foreach(var h in handlers) h(eventData);
        }
    }
}

// 使用示例
EventBus.Subscribe<HealthData>(data => {
    // 处理健康数据...
});
2. 条件过滤器
public class FilteredObserver : IObserver {
    private Predicate<HealthData> _filter;
    private Action<HealthData> _action;
    
    public FilteredObserver(Predicate<HealthData> filter, Action<HealthData> action) {
        _filter = filter;
        _action = action;
    }
    
    public void OnNotify(HealthData data) {
        if(_filter(data)) _action(data);
    }
}

// 使用示例:仅在血量低于30%时触发
var lowHealthObserver = new FilteredObserver(
    data => data.CurrentHealth/data.MaxHealth < 0.3f,
    data => ShowWarning()
);
3. 异步事件处理
public class AsyncEventProcessor : MonoBehaviour {
    private Queue<HealthData> _eventQueue = new();
    
    public void QueueEvent(HealthData data) {
        _eventQueue.Enqueue(data);
    }
    
    void Update() {
        while(_eventQueue.Count > 0) {
            StartCoroutine(ProcessEvent(_eventQueue.Dequeue()));
        }
    }
    
    private IEnumerator ProcessEvent(HealthData data) {
        // 异步处理逻辑
        yield return null;
    }
}

五、游戏开发典型应用场景
  1. 成就系统触发

    public class AchievementSystem : IObserver {
        public void OnNotify(EnemyDeathData data) {
            if(data.EnemyType == EnemyType.Boss) {
                UnlockAchievement("BOSS_SLAYER");
            }
        }
    }
    
  2. 全局事件通知

    public class GlobalEvent : ISubject {
        private static GlobalEvent _instance;
        public static GlobalEvent Instance => _instance ??= new GlobalEvent();
        
        // 实现观察者注册/通知逻辑...
    }
    
  3. 技能冷却系统

    public class SkillManager : IObserver {
        public void OnNotify(SkillEventData data) {
            if(data.EventType == SkillEventType.Used) {
                StartCooldown(data.SkillID);
            }
        }
    }
    
  4. 环境互动反馈

    public class EnvironmentFX : IObserver {
        public void OnNotify(PlayerMoveData data) {
            if(data.IsInWater) PlayWaterRippleFX(data.Position);
        }
    }
    

六、性能优化策略
策略实现方式适用场景
事件过滤前置条件检查高频事件
批处理合并多个事件物理系统更新
对象池重用事件对象频繁事件触发
分层处理优先级队列关键事件优先

七、模式对比与选择
维度观察者模式发布-订阅模式
耦合度观察者直接注册到主题通过中间件解耦
灵活性需要知道具体主题无需知道发布者
性能直接调用更高效中间件可能引入开销
典型应用组件间直接通信系统级事件管理

八、最佳实践原则
  1. 避免过度通知:仅在状态真正变化时触发通知
    private float _previousHealth;
    
    void Update() {
        if(Mathf.Abs(_currentHealth - _previousHealth) > 0.01f) {
            NotifyObservers();
            _previousHealth = _currentHealth;
        }
    }
    
  2. 内存管理:及时取消不再需要的订阅
    void OnDestroy() {
        playerHealth.RemoveObserver(this);
    }
    
  3. 线程安全:在多线程环境使用锁机制
    private readonly object _lock = new object();
    public void RegisterObserver(IObserver observer) {
        lock(_lock) {
            _observers.Add(observer);
        }
    }
    
  4. 事件数据不可变
    public readonly struct ImmutableHealthData {
        public readonly float Current;
        public ImmutableHealthData(float current) => Current = current;
    }
    

九、常见问题解决方案

Q1:如何处理循环通知?
→ 实现事件标记防止递归

private bool _isNotifying;
public void NotifyObservers() {
    if(_isNotifying) return;
    _isNotifying = true;
    // 通知逻辑...
    _isNotifying = false;
}

Q2:如何优化大量观察者的性能?
→ 使用分层观察者

public class TieredObserverSystem {
    private Dictionary<EventPriority, List<IObserver>> _tiers = new();
    
    public void NotifyByPriority() {
        foreach(var tier in Enum.GetValues(typeof(EventPriority))) {
            foreach(var observer in _tiers[(EventPriority)tier]) {
                observer.OnNotify();
            }
        }
    }
}

Q3:如何调试复杂事件流?
→ 实现事件追踪器

public class EventDebugger : IObserver {
    public void OnNotify(object data) {
        Debug.Log($"[Event] {DateTime.Now:HH:mm:ss.fff} - {data.GetType().Name}");
        // 记录到文件或调试窗口...
    }
}

上一篇 【行为型之备忘录模式】游戏开发实战——Unity存档系统与状态管理的终极解决方案
下一篇 【行为型之状态模式】深度剖析——Unity角色行为控制与AI决策的终极解决方案

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

神经网络语言模型(前馈神经网络语言模型)

神经网络语言模型 什么是神经网络&#xff1f;神经网络的基本结构是什么&#xff1f;输入层隐藏层输出层 神经网络为什么能解决问题&#xff1f;通用近似定理为什么需要权重和偏置&#xff1f;为什么需要激活函数&#xff1f;权重是如何确定的&#xff1f;1. 穷举2. 反向传播主…

CUDA编程——性能优化基本技巧

本文主要介绍下面三种技巧&#xff1a; 使用 __restrict__ 让编译器放心地优化指针访存想办法让同一个 Warp 中的线程的访存 Pattern 尽可能连续&#xff0c;以利用 Memory coalescing使用 Shared memory 0. 弄清Kernael函数是Compute-bound 还是 Memory-bound 先摆出一个知…

道通EVO MAX系列无人机-支持二次开发

道通EVO MAX系列无人机-支持二次开发 EVO Max 系列采用Autel Autonomy自主飞行技术&#xff0c;实现复杂环境下的全局路径规划、3D场景重建、自主绕障和返航&#xff1b;高精度视觉导航能力&#xff0c;使其在信号干扰强、信号遮挡、信号弱等复杂环境下&#xff0c;依然获得高精…

计算机网络-MPLS LDP基础实验配置

前面我们学习了LDP的会话建立、标签发布与交换、LDP的工作原理&#xff0c;今天通过一个基础实验来加深记忆。 一、LDP基础实验 实验拓扑&#xff1a; 1、IGP使用OSPF进行通告&#xff0c;使用Lookback接口作为LSR ID&#xff0c;LDP ID自动生成。 2、实验目的&#xff1a;使…

HPE ProLiant DL360 Gen11 服务器,配置 RAID 5 教程!

今天的任务&#xff0c;是帮客户的一台HPE ProLiant DL360 Gen11 服务器&#xff0c;配置RAID 5。依然是按照我的个人传统习惯&#xff0c;顺便做一个教程&#xff0c;分享给有需要的粉丝们。如果你在实际操作中&#xff0c;遇到了什么问题&#xff0c;欢迎在评论区留言&#x…

SARIMA-LSTM融合模型对太阳黑子数量预测分析|附智能体数据代码

全文智能体链接&#xff1a;https://tecdat.cn/?p41969 分析师&#xff1a;Peng Fan 本研究以太阳黑子活动数据为研究对象&#xff0c;旨在帮助客户探索其未来走势并提供预测分析。首先&#xff0c;通过对数据的清洗和处理&#xff0c;包括离群值的识别与处理以及时间序列的建…

C# WinForm DataGridView 非常频繁地更新或重新绘制慢问题及解决

非常频繁地更新 DataGridView问题描述&#xff1a; 在 C# 中无法在合理的时间内刷新我的 DataGridView &#xff0c;我每秒通过网络发送 20 个数据包&#xff0c;获取数据。我想解析这些数据并将其放入 DataGridView 中。我还想调整 DataGridView 的更新间隔&#xff0c;从 0.1…

【数据结构】红黑树(C++)

目录 一、红黑树的概念 二、红黑树的性质 三、红黑树结点定义 四、红黑树的操作 1. 插入操作 1.1 插入过程 1.2 调整过程 1.2.1 叔叔节点存在且为红色 1.2.2 叔叔节点存在且为黑色 1.2.3 叔叔节点不存在 2. 查找操作 2.1 查找逻辑 2.2 算法流程图 2.3 使用示例 …

Android Framework学习五:APP启动过程原理及速度优化

文章目录 APP启动优化概述APP启动流程点击图片启动APP的过程启动触发Zygote 与应用进程创建Zygote进程的创建应用进程初始化 ApplicationActivity 启动与显示 优化启动时黑白屏现象可优化的阶段Application阶段相关优化 Activity阶段数据加载阶段 Framework学习系列文章 APP启动…

Meta的AIGC视频生成模型——Emu Video

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍Meta的视频生成模型Emu Video&#xff0c;作为Meta发布的第二款视频生成模型&#xff0c;在视频生成领域发挥关键作用。 &#x1f33a;优质专栏回顾&am…

Axure难点解决分享:统计分析页面引入Echarts示例动态效果

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:统计分析页面引入Echarts示例动态效果 主要内容:echart示例引入、大小调整、数据导入 应用场景:统计分析页面…

Docker 常见问题及其解决方案

一、安装与启动问题 1.1 安装失败 在不同操作系统上安装 Docker 时&#xff0c;可能会出现安装失败的情况。例如&#xff0c;在 Ubuntu 系统中&#xff0c;执行安装命令后提示依赖缺失。这通常是因为软件源配置不正确或系统缺少必要的依赖包。 解决方案&#xff1a; 确保系统…

IC解析之TPS92682-Q1(汽车LED灯控制IC)

目录 1 IC特性介绍2 主要参数3 接口定义4 工作原理分析TPS92682-Q1架构工作模式典型应用通讯协议 控制帧应答帧协议5 总结 1 IC特性介绍 TPS92682 - Q1 是德州仪器&#xff08;TI&#xff09;推出的一款双通道恒压横流控制器&#xff0c;同时还具有各种电器故障保护&#xff0c…

6.01 Python中打开usb相机并进行显示

本案例介绍如何打开USB相机并每隔100ms进行刷新的代码,效果如下: 一、主要思路: 1. 打开视频流、读取帧 self.cam_cap = cv2.VideoCapture(0) #打开 视频流 cam_ret, cam_frame = self.cam_cap.read() //读取帧。 2.使用定时器,每隔100ms读取帧 3.显示到Qt的QLabel…

2023华为od统一考试B卷【二叉树中序遍历】

前言 博主刷的华为机考题&#xff0c;代码仅供参考&#xff0c;因为没有后台数据&#xff0c;可能有没考虑到的情况 如果感觉对你有帮助&#xff0c;请点点关注点点赞吧&#xff0c;谢谢你&#xff01; 题目描述 思路 0.用Character数组存储树&#xff0c;index下标的左右…

在Spark搭建YARN

&#xff08;一&#xff09;什么是SparkONYarn模式 Spark on YARN&#xff08;Yet Another Resource Negotiator&#xff09;是 Spark 框架在 Hadoop 集群中运行的一种部署模式&#xff0c;它借助 Hadoop YARN 来管理资源和调度任务。 架构组成 ResourceManager&#xff1a;作…

LeetCode_sql刷题(3482.分析组织层级)

题目描述&#xff1a;3482. 分析组织层级 - 力扣&#xff08;LeetCode&#xff09; 表&#xff1a;Employees ------------------------- | Column Name | Type | ------------------------- | employee_id | int | | employee_name | varchar | | manager_id …

不用服务器转码,Web端如何播放RTSP视频流?

在物联网、智慧城市、工业互联网等新兴技术浪潮下&#xff0c;实时视频流&#xff08;如RTSP协议&#xff09;作为安防监控、生产巡检、远程协作等场景的核心数据载体&#xff0c;其价值愈发凸显。然而&#xff0c;一个长期困扰行业的痛点始终存在——‌如何在Web浏览器中直接播…

如何开发一款 Chrome 浏览器插件

Chrome是由谷歌开发的网页浏览器&#xff0c;基于开源软件&#xff08;包括WebKit和Mozilla&#xff09;开发&#xff0c;任何人都可以根据自己需要使用、修改或增强它的功能。Chrome凭借着其优秀的性能、出色的兼容性以及丰富的扩展程序&#xff0c;赢得了广大用户的信任。市场…

GitHub打开缓慢甚至失败的解决办法

在C:\Windows\System32\drivers\etc的hosts中增加如下内容&#xff1a; 20.205.243.166 github.com 199.59.149.236 github.global.ssl.fastly.net185.199.109.153 http://assets-cdn.github.com 185.199.108.153 http://assets-cdn.github.com 185.199.110.153 http://asset…