创游世界广播驱动项目架构实战
一句话摘要
广播是创游世界项目解耦的核心手段,但滥用广播会导致事件来源不清、维护困难。本文从实战角度讲解广播的分类方法、命名规范、作用域设计,以及常见反模式与正确做法。
适合谁阅读
- 已经理解广播基础概念,想在实际项目中正确使用广播的制作者
- 项目逐渐复杂,开始出现“广播满天飞”问题的开发者
- 想学习如何用广播配合组件、变量、UI 构建可维护架构的学习者
你将学到什么
- 广播的三层分类方法
- 广播的命名规范与约定
- 如何用广播配合数据层分离
- 广播的常见反模式与正确做法
- 完整的项目架构示例
核心结论
- 广播应该按用途分类,而不是按功能堆叠
- 广播名称应该包含事件语义和目标层级
- 广播不适合替代数据持久化
- 广播应该是单向事件流,不是双向通信
- 大量同层级广播应考虑合并或升级为中间层
背景说明
在创游世界中,广播是跨对象通信的主要手段。它允许一个对象触发事件,其他对象监听到并响应,而不需要直接引用对方。
但在实际项目中,很多人会陷入两个极端:
- 要么完全不用广播,所有逻辑都写在一起
- 要么滥用广播,每个小动作都发广播,导致事件来源不清、调试困难
正确的做法是理解广播的本质:它是事件通知机制,不是数据存储机制,也不是逻辑执行机制。
基础概念
什么是广播
广播是一种消息发布-订阅机制:
- 发布者通过「发送广播」指令发出一个消息
- 订阅者通过「当收到广播」触发器监听消息
- 收到消息后,订阅者执行对应逻辑
广播 vs 其他通信方式
| 通信方式 | 适用场景 | 不适用场景 |
|---|---|---|
| 广播 | 解耦、跨对象事件通知、UI刷新触发 | 需要返回值、需要确认处理结果 |
| 直接调用组件方法 | 同对象内的逻辑调用 | 跨对象业务逻辑 |
| 变量读写 | 数据共享、状态持久化 | 事件通知 |
广播的三个层级
按作用范围,广播可分为:
- 对象内广播:在同一对象内部使用广播解耦逻辑
- 地图内广播:在同一地图内跨对象通信
- 全局广播:跨地图通信,通常用于存档、云变量同步
广播分类方法
推荐的三层分类
第一类:状态变更广播
描述:对象状态发生变更,通知相关方刷新
命名规范:状态_对象_变更类型
示例:
道具_获得_玩家生命_减少_怪物金币_增加_玩家
第二类:UI刷新广播
描述:数据层变化后,通知UI层刷新显示
命名规范:UI_刷新_目标
示例:
UI_刷新_背包格子UI_刷新_血条UI_刷新_商店列表
第三类:业务事件广播
描述:游戏业务逻辑中的重要事件,用于触发后续处理
命名规范:事件_业务_描述
示例:
事件_任务完成_玩家事件_商店购买_完成事件_战斗结束_结算
广播名称示例
markdown
✅ 正确示例:
- 道具_获得_玩家
- UI_刷新_背包格子
- 事件_任务完成_玩家
❌ 不推荐示例:
- 刷新(太泛)
- 更新(语义不清)
- 通知(没有目标)项目架构示例
最小可用的广播架构
假设我们要做一个背包系统:
玩家对象
├── 属性:背包数据(JSON格式存储物品ID和数量)
├── 脚本A:当获得物品时执行
│ ├── 解析物品ID和数量
│ ├── 更新背包数据
│ ├── 发送广播「道具_获得_玩家」(带参数:物品ID、数量)
│ └── 发送广播「UI_刷新_背包格子」
├── 脚本B:当收到「UI_刷新_背包格子」时执行
│ ├── 读取背包数据
│ ├── 遍历并渲染格子UI
│ └── 绑定格子点击事件
└── 脚本C:当收到「道具_获得_玩家」时执行
└── 显示获得物品的浮动提示这个架构的特点:
- 数据层(背包数据)和显示层(UI格子)分离
- 广播用于通知状态变更,不承担数据传递职责
- 每个脚本职责单一,容易维护
进阶架构:多层中间层
当项目变大时,可以引入中间层:
全局管理器对象
├── 脚本A:管理所有「事件_」类广播
│ ├── 维护全局状态
│ ├── 处理跨对象业务逻辑
│ └── 发出「状态_」类广播
└── 脚本B:管理所有「UI_」类广播
├── 监听状态变更
└── 统一刷新相关UI这样做的好处:
- 业务逻辑集中在管理器
- 对象脚本只负责监听和执行
- 方便后续扩展和调试
常见反模式与正确做法
反模式1:广播内嵌业务逻辑
markdown
❌ 反模式:
当获得道具时:
发送广播「获得道具」,参数:物品ID
当收到「获得道具」时:
如果参数.物品ID == 1:
金币加100
显示提示
如果参数.物品ID == 2:
生命加50
显示提示这种写法把业务逻辑分散在多个监听点,难以维护。
markdown
✅ 正确做法:
当获得道具时:
道具ID = 参数.物品ID
更新背包数据(道具ID、数量)
发送广播「数据_背包_更新」,参数:道具ID、数量
发送广播「UI_刷新_背包格子」
当收到「数据_背包_更新」时:
调用「处理道具逻辑」,参数:道具ID、数量反模式2:广播嵌套广播
markdown
❌ 反模式:
当收到广播A时:
发送广播B
当收到广播B时:
发送广播C
当收到广播C时:
执行实际逻辑广播嵌套会导致事件链路不清晰,难以追踪数据流向。
markdown
✅ 正确做法:
当获得道具时:
更新数据
发送广播「数据_更新」(一次广播,携带完整数据)
当收到「数据_更新」时:
刷新UI(如果需要)
显示提示(如果需要)反模式3:用广播替代数据存储
markdown
❌ 反模式:
当获得道具时:
发送广播「道具数据」,参数:道具ID、数量
当收到「道具数据」时:
显示道具(但不存储)广播是事件通知,不是数据存储。数据应该存储在变量或组件属性中。
markdown
✅ 正确做法:
当获得道具时:
更新背包变量(数据存储)
发送广播「数据_更新」(通知相关方)广播命名规范总结
| 类型 | 命名格式 | 示例 |
|---|---|---|
| 状态变更 | 实体_状态_操作 | 道具_获得_玩家、生命_减少_怪物 |
| UI刷新 | UI_刷新_目标 | UI_刷新_背包格子、UI_刷新_血条 |
| 业务事件 | 事件_业务_描述 | 事件_任务完成_玩家、事件_战斗结束_结算 |
| 系统广播 | 系统_功能_操作 | 系统_存档_保存、系统_初始化_完成 |
广播使用检查清单
在发送广播之前,检查以下问题:
- [ ] 这个广播是否描述了一个真实的事件?
- [ ] 广播名称是否能让人一眼看出含义?
- [ ] 是否有其他对象会监听这个广播?
- [ ] 这个广播是否需要参数?参数是否必要?
- [ ] 是否可以用变量替代这个广播?
- [ ] 是否在用广播替代数据存储?
相关页面
待验证问题
[待验证]在复杂项目中,广播的性能开销是否有明显影响?[待验证]是否有广播数量的合理上限建议?
后续优化方向
- 可以补充具体项目的完整广播链路图
- 可以补充调试广播的常用方法
- 可以补充广播与事件系统的对比分析
