当前位置: 首页 > article >正文

别再滥用单例了!试试Unity中的事件总线(Event Bus)模式,轻松实现组件间通信

告别单例依赖用事件总线重构Unity组件通信架构在Unity项目开发中我们经常遇到这样的场景背包系统需要更新UI提示角色受伤要触发音效播放任务完成需要通知多个系统更新状态。面对这些跨组件的通信需求很多开发者会条件反射地掏出单例模式这把万能钥匙。但当你发现单元测试越来越难写、代码依赖越来越混乱时是时候重新审视这个选择了。1. 单例模式的困境与事件总线的曙光1.1 单例模式的三大原罪让我们先解剖一个典型场景假设游戏中有一个AchievementManager单例负责处理成就系统。当玩家拾取特殊物品时背包系统需要调用AchievementManager.Instance.Unlock(TreasureCollector)当BOSS被击败时战斗系统又调用同样的单例记录进度。表面上看这很方便但隐患已经埋下// 典型单例调用示例 public class BackpackSystem : MonoBehaviour { void AddItem(Item item) { if(item.isRare) { AchievementManager.Instance.Unlock(RareItemFound); SoundManager.Instance.Play(RareItemSFX); UIManager.Instance.ShowToast(获得稀有物品); } } }这种写法存在三个致命问题强耦合每个调用方都直接依赖具体单例类修改AchievementManager接口会影响所有调用者测试困难无法为BackpackSystem编写独立单元测试必须实例化所有单例职责混乱业务逻辑分散在各个调用点难以追踪事件流向1.2 事件总线的工作机制事件总线模式采用完全不同的思路。它引入一个中间人Mediator来管理事件流转[背包系统] --发布-- [事件总线] --通知-- [成就系统] [事件总线] --通知-- [音效系统] [事件总线] --通知-- [UI系统]这种架构下发布者不需要知道谁在处理事件处理器也不需要知道事件来源。用实际代码对比// 事件总线版本 public class BackpackSystem : MonoBehaviour { void AddItem(Item item) { if(item.isRare) { EventBus.Publish(new RareItemFoundEvent(item)); } } } // 成就系统 public class AchievementSystem : MonoBehaviour { void OnEnable() { EventBus.SubscribeRareItemFoundEvent(OnRareItemFound); } void OnRareItemFound(RareItemFoundEvent e) { Unlock(RareItemFound); } }2. 实现高性能事件总线系统2.1 基础事件总线实现一个健壮的事件总线需要处理多种事件类型。以下是支持泛型的实现方案public static class EventBus { private static readonly DictionaryType, Delegate _handlers new(); public static void SubscribeT(ActionT handler) where T : IEvent { if(_handlers.TryGetValue(typeof(T), out var existing)) { _handlers[typeof(T)] Delegate.Combine(existing, handler); } else { _handlers[typeof(T)] handler; } } public static void PublishT(T event) where T : IEvent { if(_handlers.TryGetValue(typeof(T), out var handlers)) { (handlers as ActionT)?.Invoke(event); } } } public interface IEvent {}2.2 性能优化技巧基础实现虽然可用但在大型项目中可能需要优化事件缓存对高频事件对象使用对象池public class EventPoolT where T : IEvent, new() { private static readonly QueueT _pool new(); public static T Get() _pool.Count 0 ? _pool.Dequeue() : new T(); public static void Release(T e) _pool.Enqueue(e); }线程安全处理private static readonly object _lock new(); public static void PublishT(T event) { lock(_lock) { // 发布逻辑 } }性能对比表格方案每秒调用次数GC分配直接调用10,000,0000B基础事件总线2,500,00048B优化后事件总线3,800,00016B3. 实战重构任务系统3.1 传统单例实现的问题考虑一个任务系统需要与多个模块交互public class QuestSystem : MonoBehaviour { public void CompleteQuest(int questId) { // 单例调用方式 ProgressTracker.Instance.UpdateQuest(questId); RewardManager.Instance.GrantRewards(questId); AnalyticsManager.Instance.LogQuestComplete(questId); UIManager.Instance.ShowQuestComplete(questId); } }这种实现方式导致添加新响应逻辑需要修改QuestSystem难以模拟依赖进行测试调用链不透明难以调试3.2 事件总线重构方案首先定义事件类型public struct QuestCompletedEvent : IEvent { public int QuestId; public DateTime CompleteTime; }然后重构任务系统public class QuestSystem : MonoBehaviour { public void CompleteQuest(int questId) { EventBus.Publish(new QuestCompletedEvent { QuestId questId, CompleteTime DateTime.Now }); } }各消费方独立订阅// 进度系统 public class ProgressSystem : MonoBehaviour { void OnEnable() { EventBus.SubscribeQuestCompletedEvent(OnQuestCompleted); } void OnQuestCompleted(QuestCompletedEvent e) { UpdateQuestProgress(e.QuestId); } }4. 高级应用场景与陷阱规避4.1 跨场景事件处理Unity的场景加载机制会销毁GameObject导致事件订阅丢失。解决方案持久化事件总线public class EventBusHolder : MonoBehaviour { static EventBusHolder _instance; void Awake() { if(_instance ! null) { Destroy(gameObject); return; } _instance this; DontDestroyOnLoad(gameObject); } }场景卸载时自动取消订阅public class SafeSubscriber : MonoBehaviour { readonly ListDelegate _subscriptions new(); public void SubscribeT(ActionT handler) { EventBus.Subscribe(handler); _subscriptions.Add(handler); } void OnDestroy() { foreach(var sub in _subscriptions) { // 实现对应的取消订阅逻辑 } } }4.2 常见陷阱与解决方案内存泄漏忘记取消订阅会导致对象无法被GC回收提示始终在OnDisable中取消订阅或使用WeakReference事件循环A事件触发B事件B事件又触发A事件// 检测循环事件 public static void PublishT(T event) { if(_currentPublishing.Contains(typeof(T))) { Debug.LogError($事件循环检测: {typeof(T)}); return; } _currentPublishing.Push(typeof(T)); try { // 正常发布逻辑 } finally { _currentPublishing.Pop(); } }性能热点高频事件可能成为性能瓶颈使用事件合并将多个相似事件合并处理添加节流机制限制事件触发频率事件总线不是银弹但在处理组件间通信时它比单例模式提供了更好的解耦方案。在我的一个中型RPG项目中将核心系统改为事件驱动后单元测试覆盖率从35%提升到了72%系统间的依赖关系减少了60%。当你的项目开始出现单例依赖症时不妨试试这种更优雅的解决方案。

相关文章:

别再滥用单例了!试试Unity中的事件总线(Event Bus)模式,轻松实现组件间通信

告别单例依赖:用事件总线重构Unity组件通信架构 在Unity项目开发中,我们经常遇到这样的场景:背包系统需要更新UI提示,角色受伤要触发音效播放,任务完成需要通知多个系统更新状态。面对这些跨组件的通信需求&#xff0c…...

Windows任务栏透明化终极指南:TranslucentTB深度解析与专业配置

Windows任务栏透明化终极指南:TranslucentTB深度解析与专业配置 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB 想要彻底改造…...

如何快速掌握Can-I-Take-Over-XYZ:自定义指纹与多线程检测完整指南

如何快速掌握Can-I-Take-Over-XYZ:自定义指纹与多线程检测完整指南 【免费下载链接】can-i-take-over-xyz "Can I take over XYZ?" — a list of services and how to claim (sub)domains with dangling DNS records. 项目地址: https://gitcode.com/g…...

Silero Models vs Kaldi:现代语音处理框架的终极对比指南

Silero Models vs Kaldi:现代语音处理框架的终极对比指南 【免费下载链接】silero-models Silero Models: pre-trained text-to-speech models made embarrassingly simple 项目地址: https://gitcode.com/gh_mirrors/si/silero-models 在当今快速发展的语音…...

零基础入门AI:收藏这份“造”与“用”的工程师指南,抓住大模型红利!

文章对比了传统算法工程师与AI大模型应用开发工程师两大AI领域“门派”。传统算法工程师是AI基建者,专注数学与编程,通过逻辑解决具体问题;大模型应用开发工程师则是场景魔术师,擅长利用现成大模型解决业务落地难题。文章强调大模…...

如何快速掌握Can-I-Take-Over-XYZ:子域名接管防御与自动化指纹校验完整指南

如何快速掌握Can-I-Take-Over-XYZ:子域名接管防御与自动化指纹校验完整指南 【免费下载链接】can-i-take-over-xyz "Can I take over XYZ?" — a list of services and how to claim (sub)domains with dangling DNS records. 项目地址: https://gitco…...

Obsidian Tasks 任务优先级终极指南:6个等级助你高效管理待办事项

Obsidian Tasks 任务优先级终极指南:6个等级助你高效管理待办事项 【免费下载链接】obsidian-tasks Task management for the Obsidian knowledge base. 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian-tasks 在Obsidian知识管理系统中,T…...

卫星通信物联网:如何构建全球覆盖的数据传输网络终极指南

卫星通信物联网:如何构建全球覆盖的数据传输网络终极指南 【免费下载链接】InterviewGuide 🔥🔥「InterviewGuide」是阿秀从校园->职场多年计算机自学过程的记录以及学弟学妹们计算机校招&秋招经验总结文章的汇总,包括但不…...

容器安全实战指南:用Trivy与Clair守护你的Searx隐私搜索引擎

容器安全实战指南:用Trivy与Clair守护你的Searx隐私搜索引擎 【免费下载链接】searx Privacy-respecting metasearch engine 项目地址: https://gitcode.com/gh_mirrors/se/searx Searx作为一款注重隐私保护的元搜索引擎,允许用户聚合多个来源的搜…...

48个编程挑战带你从入门到精通:2023编程挑战完全指南

48个编程挑战带你从入门到精通:2023编程挑战完全指南 【免费下载链接】retos-programacion-2023 Ejercicios de cdigo semanales en 2023 de la comunidad MoureDev para practicar lgica en cualquier lenguaje de programacin. 项目地址: https://gitcode.com/g…...

别只改Nginx配置!从HTTP协议层拆解206状态码与CONTENT_LENGTH_MISMATCH的坑

从HTTP协议层拆解206状态码与CONTENT_LENGTH_MISMATCH的深层逻辑 视频播放失败时控制台弹出的net::ERR_CONTENT_LENGTH_MISMATCH 206 (Partial Content)错误,往往让开发者陷入反复调整Nginx配置的循环。但真正的问题可能隐藏在HTTP协议层与数据传输机制的配合间隙中…...

React-Redux网络优化:减少HTTP请求的终极策略

React-Redux网络优化:减少HTTP请求的终极策略 【免费下载链接】react-redux Official React bindings for Redux 项目地址: https://gitcode.com/gh_mirrors/re/react-redux React-Redux作为Redux的官方React绑定库,是构建高效React应用的关键工具…...

基于Go的AI代理网关:构建稳定可扩展的Claude API服务层

1. 项目概述:一个面向AI对话模型的智能代理网关最近在折腾AI应用开发,特别是想把Claude、GPT这些大模型的能力集成到自己的产品里,发现一个挺头疼的问题:不同模型的API接口、认证方式、计费模式五花八门,管理起来特别麻…...

构建基于 Taotoken 与 Node 的自动化内容处理微服务

构建基于 Taotoken 与 Node 的自动化内容处理微服务 1. 场景需求与架构设计 在现代化应用开发中,文本内容的自动化生成与处理已成为常见需求。例如电商平台需要动态生成商品描述,社交媒体工具需辅助用户润色帖子内容,或企业内部系统要自动汇…...

ChatGPT API响应延迟优化实战:连接池与流式处理提升交互体验

1. 项目概述:当ChatGPT“卡顿”时,我们到底在修复什么?如果你经常使用基于OpenAI API构建的各类应用,无论是自己开发的聊天机器人、集成到工作流中的智能助手,还是第三方客户端,大概率都遇到过这种情况&…...

从监控碎片化到统一流媒体:go2rtc如何重新定义摄像头管理体验?

从监控碎片化到统一流媒体:go2rtc如何重新定义摄像头管理体验? 【免费下载链接】go2rtc Ultimate camera streaming application 项目地址: https://gitcode.com/GitHub_Trending/go/go2rtc 你是否曾经为家里各种品牌的摄像头无法统一管理而烦恼&…...

React Native应用架构设计终极指南:Deco IDE助你构建大型项目

React Native应用架构设计终极指南:Deco IDE助你构建大型项目 【免费下载链接】deco-ide The React Native IDE 项目地址: https://gitcode.com/gh_mirrors/de/deco-ide 在移动应用开发领域,React Native以其跨平台优势和高效开发流程赢得了众多开…...

06华夏之光永存・开源:黄大年茶思屋31期全解题战略总结篇

06华夏之光永存・开源:黄大年茶思屋31期全解题战略总结篇 【破界登顶全域领跑:全解之后,华为筑牢全球科技绝对霸权】 当全球科技巨头还在传统技术框架里做着无关痛痒的局部优化,在性能天花板下苦苦挣扎、陷入技术内卷无法破局时&a…...

终极指南:使用Sass HiDPI为Retina显示器优化网站图片

终极指南:使用Sass HiDPI为Retina显示器优化网站图片 【免费下载链接】hidpi Serve high resolution graphics to high density (Retina-like) displays with Sass. 项目地址: https://gitcode.com/gh_mirrors/hi/hidpi 在当今高分辨率设备普及的时代&#x…...

告别跷跷板效应:手把手教你用PaddlePaddle复现腾讯PLE多任务推荐模型

从零实现腾讯PLE模型:用PaddlePaddle解决多任务推荐中的跷跷板难题 推荐系统发展到今天,早已不再是简单的协同过滤或矩阵分解就能满足业务需求。当我们需要同时优化点击率、观看时长、分享率等多个目标时,传统的单任务学习模型往往捉襟见肘。…...

5大场景深度解析:PiliPlus开源B站客户端的跨平台体验革新

5大场景深度解析:PiliPlus开源B站客户端的跨平台体验革新 【免费下载链接】PiliPlus PiliPlus 项目地址: https://gitcode.com/gh_mirrors/pi/PiliPlus PiliPlus是一款基于Flutter开发的跨平台Bilibili第三方客户端,为技术爱好者和普通用户提供纯…...

05华夏之光永存・开源:黄大年茶思屋榜文保姆级全落地解法「31期 5题」多模态高维数据解耦可控生成+AI极致视频压缩

05华夏之光永存・开源:黄大年茶思屋榜文保姆级全落地解法「31期 5题」 【多模态高维数据解耦可控生成AI极致视频压缩|纯全落地全裸参数开源上机直接跑版】 全落地・全参数开源・保姆级上机可跑版 一、摘要 多模态特征可控生成、视频智能压缩领域&#xf…...

教育科技产品集成Taotoken为不同学科场景匹配最合适的大模型

教育科技产品集成Taotoken为不同学科场景匹配最合适的大模型 1. 教育科技产品的模型需求特点 教育科技产品通常需要覆盖多个学科领域,每个领域对模型能力的要求差异显著。数学辅导需要强大的逻辑推理和分步解题能力,语言学习依赖准确的语法分析和发音评…...

揭秘AI专著撰写:实用AI工具,一键打造20万字专业学术专著!

学术专著创作困境与AI工具的崛起 学术专著的创作过程并不简单,其难点不仅在于“能写出来”,更在于“能够出版并获得认可”。在当今的出版市场中,学术专著面临的受众群体相对较少,出版社对于选题的学术价值和作者的学术声望有着严…...

EBERLE S-41/051413016000印刷电路板

EBERLE S-41/051413016000 通常属于其工业自动化系统中的印刷电路板(PCB)组件 根据此类专用电路板的通用特性,推测其特点如下:定制化设计:推测是针对特定控制任务(如信号转换、电源管理或接口扩展&#xff…...

7+ Taskbar Tweaker疑难杂症终极指南:从症状到根除的完整解决方案

7 Taskbar Tweaker疑难杂症终极指南:从症状到根除的完整解决方案 【免费下载链接】7-Taskbar-Tweaker A Windows taskbar customization tool for Windows 7, Windows 8, and Windows 10 项目地址: https://gitcode.com/gh_mirrors/7t/7-Taskbar-Tweaker 7 T…...

不只是安装:用PyTorch-Kaldi + LibriSpeech快速搭建你的第一个端到端语音识别实验环境

从Kaldi到PyTorch-Kaldi:构建端到端语音识别实验环境的完整指南 语音识别技术正在经历从传统方法到深度学习的快速演进。对于已经掌握Kaldi基础的研究者来说,如何将这一强大工具与现代深度学习框架结合,构建端到端的实验环境,是迈…...

AI编码助手集成Revnu技能:自动化软件销售与商业运营指南

1. 项目概述:让AI编码助手成为你的软件销售经理 如果你是一名独立开发者、小团队负责人,或者正在用AI辅助构建自己的SaaS产品,那么你肯定遇到过这样的困境:产品做出来了,但如何优雅地、自动化地把它卖出去&#xff0c…...

SpringBoot 3.1.3 + JDK 17 实战:手把手教你从零搭建一个OAuth2.1授权服务器

SpringBoot 3.1.3 JDK 17 实战:从零构建OAuth2.1授权服务器的完整指南 在当今微服务架构盛行的时代,安全认证已成为系统设计的核心环节。OAuth2.1作为OAuth2.0的进化版本,针对实际应用中的安全漏洞和模糊定义进行了重要修正。本文将带您使用…...

Taotoken 模型广场如何帮助开发者为新项目选型

Taotoken 模型广场如何帮助开发者为新项目选型 启动一个新项目时,选择合适的模型是技术决策的关键一步。面对市场上众多的模型提供商和复杂的定价体系,开发者往往需要花费大量时间调研。Taotoken 的模型广场功能,正是为了简化这一过程而设计…...