# 02 · 事件与消息系统规范 > **所属文档集** [← 返回索引](./README.md) > **摘要**:定义游戏全局的事件通信规范、事件目录与发布/订阅契约。 --- ## 目录 1. [事件系统设计原则](#1-事件系统设计原则) 2. [事件类型体系](#2-事件类型体系) 3. [发布/订阅契约](#3-发布订阅契约) 4. [全局事件目录](#4-全局事件目录) 5. [事件设计指导](#5-事件设计指导) --- ## 1. 事件系统设计原则 | 原则 | 说明 | |------|------| | **最小载荷** | 事件只携带消费方需要的最少信息 | | **类型安全** | 每个事件有明确的载荷类型,不使用无类型通用事件 | | **无副作用传递** | 事件不携带可修改的对象引用,防止订阅方意外修改状态 | | **单向通知** | 事件只通知"发生了什么",不携带"如何处理"的指令 | | **有限生命周期** | 事件在发出后立即处理,不在系统间长期持有 | --- ## 2. 事件类型体系 ### 2.1 按载荷分类 | 类型 | 描述 | 适用场景 | |------|------|---------| | `Event` | 无载荷事件,纯通知 | 玩家死亡、游戏暂停 | | `Event` | 单值载荷 | HP 变化(传入新值)| | `Event` | 结构体载荷 | 伤害事件(多字段)| ### 2.2 按作用范围分类 | 类型 | 作用范围 | 说明 | |------|---------|------| | **全局事件** | 全游戏有效,跨场景 | 玩家死亡、游戏暂停、存档完成 | | **局部事件** | 单场景/房间内有效 | 敌人死亡、机关触发、Boss 阶段变更 | | **实体事件** | 单个实体范围 | 单个 NPC 对话开始、单个陷阱激活 | --- ## 3. 发布/订阅契约 ### 3.1 发布方契约 发布方(Emitter)必须: - 在系统初始化时注册事件频道 - 仅在状态确实发生变化时发出事件(不重复发出相同状态) - 在系统销毁时清理事件频道 ### 3.2 订阅方契约 订阅方(Listener)必须: - 在系统初始化时注册订阅 - 在系统销毁时取消订阅(防止悬挂引用) - 不在事件处理中再发出同类型事件(防止递归循环) ### 3.3 事件处理顺序 当多个系统订阅同一事件时,处理顺序**不保证**。 任何依赖特定顺序的逻辑应通过**新增中间事件**拆分为有序的因果链,而非依赖底层执行顺序。 ``` ❌ 错误:期望 A 先处理,B 后处理同一事件 ✅ 正确:A 处理事件后发出新事件,B 订阅新事件 ``` --- ## 4. 全局事件目录 ### 4.1 玩家相关事件 | 事件名 | 载荷类型 | 发出方 | 典型订阅方 | 触发时机 | |--------|---------|--------|----------|---------| | `OnPlayerDied` | `void` | 玩家系统 | UI、音频、存档、世界 | 玩家 HP 归零 | | `OnPlayerRevived` | `void` | 游戏管理器 | 玩家系统、UI、音频 | 复活流程完成 | | `OnHPChanged` | `{current: Integer, max: Integer}` | 玩家系统 | UI(HUD)| HP 任意变化 | | `OnSoulPowerChanged` | `{current: Integer, max: Integer}` | 玩家系统 | UI(HUD)| 灵力变化 | | `OnSpiritPowerChanged` | `{current: Integer, max: Integer}` | 玩家系统 | UI(HUD)| 魄元变化 | | `OnSpringChargesChanged` | `{current: Integer, max: Integer}` | 玩家系统 | UI(HUD)| 灵泉次数变化 | | `OnGeoChanged` | `{current: Integer, delta: Integer}` | 经济系统 | UI(HUD)| Geo 增减 | | `OnAbilityUnlocked` | `{abilityId: ID}` | 进程系统 | 玩家系统、UI | 新能力获得 | | `OnFormChanged` | `{newFormId: ID}` | 形态系统 | UI、音频、武器系统 | 玩家切换形态 | | `OnParrySuccess` | `{counterWindowDuration: Duration}` | 战斗系统 | 玩家系统、UI、音频 | 弹反判定成功 | | `OnHitConfirmed` | `HitPayload` | 战斗系统 | 玩家系统(灵力积累)、反馈系统 | 攻击命中判定 | ### 4.2 敌人相关事件 | 事件名 | 载荷类型 | 发出方 | 典型订阅方 | 触发时机 | |--------|---------|--------|----------|---------| | `OnEnemyDied` | `{enemyId: ID, position: Vector2}` | 敌人系统 | 进程系统、经济系统、叙事系统 | 敌人 HP 归零 | | `OnEnemyHit` | `HitPayload` | 战斗系统 | 反馈系统、音频 | 攻击命中敌人 | | `OnBossPhaseChanged` | `{bossId: ID, phase: Integer}` | Boss 系统 | UI、音频 | Boss 进入新阶段 | | `OnBossDefeated` | `{bossId: ID}` | 敌人系统 | 进程系统、叙事系统、存档 | Boss 战胜利 | ### 4.3 世界/进程相关事件 | 事件名 | 载荷类型 | 发出方 | 典型订阅方 | 触发时机 | |--------|---------|--------|----------|---------| | `OnSceneTransitionBegin` | `{targetSceneId: ID}` | 世界系统 | UI(遮罩)、输入系统 | 开始场景切换 | | `OnSceneTransitionEnd` | `{sceneId: ID}` | 世界系统 | 输入系统、音频 | 场景切换完成 | | `OnSavePointActivated` | `{savePointId: ID, position: Vector2}` | 世界系统 | 存档系统、玩家系统、UI | 玩家激活存档点 | | `OnRoomDiscovered` | `{roomId: ID}` | 世界系统 | 地图系统 | 玩家首次进入房间 | | `OnCollectiblePickedUp` | `{collectibleId: ID, type: CollectibleType}` | 世界系统 | 进程系统、叙事系统 | 玩家拾取收集品 | | `OnWorldFlagChanged` | `{flagId: ID, value: Boolean}` | 叙事系统 | 叙事系统(自引用)、进程系统 | 世界状态标志变化 | ### 4.4 UI/系统相关事件 | 事件名 | 载荷类型 | 发出方 | 典型订阅方 | 触发时机 | |--------|---------|--------|----------|---------| | `OnGamePaused` | `void` | 游戏管理器 | 所有系统 | 暂停键触发 | | `OnGameResumed` | `void` | 游戏管理器 | 所有系统 | 恢复键触发 | | `OnSaveCompleted` | `{slotId: Integer}` | 存档系统 | UI(提示)| 存档写入完成 | | `OnSettingsChanged` | `SettingsPayload` | 设置系统 | 音频、无障碍、UI 等 | 设置项变更 | | `OnAchievementUnlocked` | `{achievementId: ID}` | 成就系统 | UI(Toast)| 成就达成 | --- ## 5. 事件设计指导 ### 5.1 新增事件时的决策流程 ``` 需要系统 A 通知系统 B 时: B 是否需要立即同步返回值? ├─ 是 → 使用接口调用(不是事件) └─ 否 → 使用事件 事件是否跨多个系统关心? ├─ 是(多个系统订阅)→ 定义为全局事件,加入全局目录 └─ 否(仅 A→B)→ 考虑是否用接口更合适 事件是否携带可变对象引用? ├─ 是 → 改为传递值类型载荷(ID + 必要字段) └─ 否 → 可以使用 ``` ### 5.2 载荷设计规范 **好的载荷(值类型,信息完整)**: ``` OnEnemyDied { enemyId : ID // 唯一标识,订阅方可用于查询更多信息 position : Vector2 // 死亡位置(生成掉落物需要) lootTableId : ID // 掉落表 ID(经济系统直接使用) } ``` **不好的载荷(引用传递,导致耦合)**: ``` OnEnemyDied { enemyRef : EnemyObject // ❌ 引用具体对象,订阅方与敌人系统耦合 } ``` ### 5.3 事件命名规范 | 规范 | 说明 | 示例 | |------|------|------| | 动词过去式(On + 名词 + 动词过去式)| 表示"已发生的事" | `OnPlayerDied`、`OnAbilityUnlocked` | | 变化类用 Changed | 状态数值变化 | `OnHPChanged`、`OnGeoChanged` | | 开始/结束对 | 持续性事件标注阶段 | `OnSceneTransitionBegin` / `OnSceneTransitionEnd` | | 避免"Will/Should" | 事件是通知,不是征询许可 | ❌ `OnPlayerWillDie`,✅ `OnPlayerDied` |