观察者模式(Observer Pattern)的核心是”发布-订阅”机制 ——— 让一个“事件源”(被观察者)在状态放生变化时, 自动通知所有”订阅者”(观察者), 且两者完全解耦(事件源不知道观察者的存在, 观察者也不依赖事件源的具体实现).
 
在Unity中, 它是处理跨系统事件通知的”神器”, 比如”玩家得分变化””角色受伤””卡关结束”等场景, 都能通过它避免代码耦合.
 

举个具体场景: 玩家得分事件通知

假设游戏中玩家得分时, 需要触发 3 个独立功能:
  1. 分数UI面板(ScoreUI): 更新显示分数
  1. 成就系统(AchievementSystem): 检查是否达成”得分 100分” “连续加分”等成就;
  1. 音效系统(SoundSystem): 播放加分音效
 
如果不用观察者模式, ScoreManager(得分管理)会直接调用这3个系统的方法(scoreUI.UpdateScore()\achievement.Check()), 导致:
  • ScoreManager 与其他系统强耦合, 修改任何一个系统都要改 ScoreManager;
  • 新增”积分排行榜”时, 需要修改 SocreManager 代码, 违反开放 - 封闭原则.
 
观察者模式解决思路:
  • ScoreManager 作为”事件源”, 只负责管理分数和发布”得分变化”事件;
  • 分数UI\成就系统作为”观察者”, 订阅该事件;
  • 得分变化时, ScoreManager 只需要通知所有订阅者, 无需关心谁在订阅.
 

观察者模式的核心角色

  1. 抽象主题(ISubject): 定义事件源的统一接口(注册观察者\移除观察者\通知观察者).
  1. 具体主题(Concrete Subject): 事件源的实际实现(如 ScoreManager), 持有观察者列表, 状态变化时触发通知.
  1. 抽象观察者(IObserver): 定义观察者统一接口(接受事件通知的方法).
  1. 具体观察者(Concrete Observer): 订阅事件的对象(如 Score UI\ AchievementSystem), 实现接受通知后的逻辑.
 

代码实现步骤

  1. 定义抽象接口(解耦核心)
先定义 ISubject (事件源接口) 和IObserver(观察者接口), 让具体类依赖抽象:
 
  1. 实现具体主题(事件源: 得分管理)
ScoreManager 负责管理分数, 当分数变化时, 通知所有已注册的观察者:
 
  1. 实现具体观察者(订阅者: 响应逻辑)
每个需要相应”分数变化”的系统, 都是先 IObserver 接口, 专注于自己的逻辑:
 
  1. 测试脚本(模式得分场景)
创建要给测试脚本, 模拟游戏中”击杀敌人加分”的逻辑:
 

Unity中配置与测试步骤

  1. 创建核心对象:
    1. 新建空对象 GameManager, 添加 ScoreManagerSocreTester脚本
  1. 创建观察者对象:
      • 新建 UI 文本(TextMeshPro),命名ScoreText,添加ScoreUI脚本,将文本组件拖到_scoreText字段。
      • 新建空对象AchievementSystem,添加AchievementSystem脚本。
      • 新建空对象SoundSystem,添加AudioSource组件(赋值加分音效),再添加SoundSystem脚本,将AudioSource拖到_addScoreSound字段。
  1. 关联引用:
    1. GameManagerScoreManager组件, 拖到ScoreTester_scoreManager字段.
  1. 运行测试:
    1. 运行后, 控制台会输出各种观察者的相应日志, 分数UI会实时更新, 音效会播放, 成就会自动解锁.
 

观察者模式的核心优势

  1. 解耦事件源与观察者: ScoreManager 不需要知道 ScoreUI\AchievementSystem的存在, 只负责发布事件; 观察者也不用依赖 ScoreManager的具体实现, 只需要订阅事件.
  1. 扩展性极强: 新增”积分呢排行榜”时, 只需新建RankSystem类实现IObserver, 注册到ScoreManager即可, 无需修改任何现有代码.
  1. 灵活控制订阅关系: 可以在运行时动态注册/移除观察者(比如暂停时移除音效观察者, 恢复后重新注册).
  1. 统一事件分发: 一次事件触发, 所有订阅者自动相应, 无需手动调用多个系统.
 

Unity中的原生替代方案

Unity自带的UnityEvent(或Event)本质上也是观察者模式的封装, 用法更简洁(无需手动定义 ISubjectIObserver), 比如将ScoreManager修改为:
然后再观察者脚本中, 直接通过 Inspector将 OnScoreChangedEvent 绑定到响应方法(或代码绑定OnScoreChangedEvent.AddListener(OnScoreChanged)), 功能完全一致, 更适合快速开发.
 

总结

观察模式就像”微信公众号” ——— 公众号(事件源 ScoreManager )发布文章(分数变化), 所有订阅的用户(观察者 ScoreUI \ AchievementSystem )都会收到通知, 公众号不用知道订阅者是谁, 订阅者也可以随时取消关注. 在Unity中, 它是处理跨系统事件\减少代码耦合的”必备工具”.