12 多数据库:模块化事件架构
从三个活动数据库运行同一条事件链,并确认最后的归档动作
适合需要组织大量事件、减少多人协作合并冲突的团队概述
发射流程从同一管理器注册的多个数据库中读取事件,打开管理器和 FlowGraph,可查看每一步所属的数据库以及跨库流程的最终结果
示例场景
Assets/TinyGiants/GameEventSystem/Demo/12_MultiDatabase/12_MultiDatabase.unity
Demo 11 把六个发射事件放在 GameEventDatabase_Chain.asset 里,Demo 12 将相同的五步流程拆进三个数据库
GameEventDatabase_Core.asset保存启动和安全检查事件GameEventDatabase_Combat.asset保存充能和发射事件GameEventDatabase_System.asset保存冷却和归档事件
数据库架构
物理资产结构

在项目窗口中:
📂 12_MultiDatabase/
│
├── 🧠 GameEventDatabase_Core.asset ➔ [ 📦 2个事件 ]
│ ├── 🎬 0_StartSequence ➔ 介绍逻辑
│ └── ⚙️ 1_SystemCheck ➔ 初始化
│
├── ⚔️ GameEventDatabase_Combat.asset ➔ [ 📦 2个事件 ]
│ ├── ⚡ 2_Charge ➔ 技能启动
│ └── 🔥 3_Fire ➔ 抛射物逻辑
│
├── 🛠️ GameEventDatabase_System.asset ➔ [ 📦 2个事件 ]
│ ├── ⏳ 4_CoolDown ➔ 全局计时器
│ └── 📁 5_Archive ➔ 持久化/保存
│
└── 🕸️ GameEventFlow_MultiDatabase.asset ➔ [ 🌐 流程图 ]
└─ (连接上述所有数据库中的事件)
这些数据库分别保存为 ScriptableObject 资产,可以独立引用和版本管理,FlowGraph 中的节点仍可连接来自三个数据库的事件
如何交互
运行时行为测试
场景在视觉上与Demo 11相同
相同的炮塔、相同的按钮、相同的发射序列
步骤1:进入播放模式
步骤2:测试正常发射
- 点击**"Launch A"**
- 观察: 事件链运行到第 5 步并完成归档
- 系统检查 → 充能(1秒延迟)→ 开火 → 冷却 → 归档
- 幕后: 执行跨越3个数据库:
- 步骤1(
SystemCheck)来自Core数据库 - 步骤3(
Fire)来自Combat数据库 - 步骤5(
Archive)来自System数据库
- 步骤1(
结果: ✅ 无缝的跨数据库执行
数据库加载验证
此测试证明模块化加载系统有效:
步骤3:禁用战斗数据库
- 在层级中选择Game Event Manager
- 在检查器中展开Databases列表
- 找到
GameEventDatabase_Combat条目 - 取消选中"Active"切换
步骤4:测试禁用的数据库
- 点击**"Launch A"**
- 结果: ❌ 序列在步骤2(充能)处挂起
- 控制台显示关于缺失事件的错误
- 步骤3-5从未执行
步骤5:重新启用战斗数据库
- 再次选中"Active"切换
- 点击**"Launch A"**
- 结果: ✅ 序列再次工作
这证明了什么:
- 数据库可以在运行时动态启用/禁用
- 缺失的数据库会中断执行(符合预期)
- 没有"自动重新加载"魔法——您控制加载的内容
多数据库配置
运行时:管理器设置
在层级中选择Game Event Manager以查看多数据库配置:

数据库列表(3个条目):
- ✅
GameEventDatabase_Core- 激活 - ✅
GameEventDatabase_Combat- 激活 - ✅
GameEventDatabase_System- 激活
运行时合并的工作原理:
注册数据库 → 读取活动数据库中的事件 → 按事件 GUID 解析各个 FlowGraph 节点
FlowGraph 按内部 GUID 从活动数据库中查找目标事件,运行演示时启用这三个数据库,事件链才能执行最后的归档步骤
设计时:编辑器数据库切换
打开游戏事件编辑器管理跨数据库的事件:

数据库下拉菜单(工具栏): 显示所有可用的数据库:
GameEventDatabase_Core(已选中)GameEventDatabase_CombatGameEventDatabase_System
工作流程:
- 选择数据库: 选择要编辑的数据库
- 查看事件: 编辑器仅显示所选数据库中的事件
- 创建事件: 新事件进入当前选中的数据库
- 切换上下文: 下拉菜单允许快速导航
示例 - 查看核心数据库:
- 下拉菜单:
GameEventDatabase_Core - 显示的事件:
0_StartSequence、1_SystemCheck(共2个) - 隐藏的事件:其他数据库中的所有事件
示例 - 查看战斗数据库:
- 下拉菜单:
GameEventDatabase_Combat - 显示的事件:
2_Charge、3_Fire(共2个) - 隐藏的事件:其他数据库中的所有事件
编辑器一次显示一个数据库以减少视觉混乱
使用下拉菜单在数据库之间切换
这不影响运行时——所有激活的数据库仍然合并
检查器:跨数据库事件选择
在检查器中为脚本分配事件时,GameEventDropdown显示来自所有激活数据库的事件:

下拉菜单结构: 事件按数据库和类别分组:
⚔️ GameEventDatabase_Combat / Default
├─ ⚡ 2_Charge
└─ ⚡ 3_Fire
🧠 GameEventDatabase_Core / Default
├─ 📍 🎬 0_StartSequence ➔ [ 当前选中 ]
└─ ⚙️ 1_SystemCheck
🛠️ GameEventDatabase_System / Default
├─ ⏳ 4_CoolDown
└─ 💾 5_Archive
下拉菜单按数据库分组:
- 所有激活的数据库: 下拉菜单包括管理器加载的每个数据库中的事件
- 数据库标签: 事件前缀数据库名称以便清晰
- 类别分组: 事件在每个数据库内按类别组织
- 类型过滤: 仅显示与字段类型签名匹配的事件
示例分配:
[GameEventDropdown]
public GameObjectDamageInfoGameEvent sequenceStartEvent;
下拉菜单显示:
- ✅
0_StartSequence(来自Core数据库)- 兼容类型 - ❌ 类型不匹配时隐藏其他事件
下拉菜单自动按以下方式过滤:
- 类型兼容性 - 仅显示与字段类型匹配的事件
- 激活的数据库 - 仅显示管理器加载的数据库中的事件
- 数据库/类别 - 分组以便轻松导航
这可以防止类型错误并使大型项目易于导航
多数据库优势
团队协作
问题: 10个开发人员都在编辑 GlobalDatabase.asset
- 持续的Git合并冲突
- 加载时编辑器长时间冻结
- 所有权不明确
解决方案: 基于模块的数据库所有权
📂 Databases/
│
├── 🧠 Core_DB.asset ➔ 💻 [ 所有者:首席程序员 ]
│ └─ 全局状态、初始化和低级触发器
│
├── ⚔️ Combat_DB.asset ➔ 🤺 [ 所有者:战斗团队 ]
│ └─ 攻击序列、AI行为和伤害逻辑
│
├── 🖥️ UI_DB.asset ➔ 🎨 [ 所有者:UI团队 ]
│ └─ 菜单转换、HUD更新和按钮反馈
│
├── 🔊 Audio_DB.asset ➔ 🎧 [ 所有者:音频团队 ]
│ └─ 环境循环、音效触发器和音乐状态切换
│
├── 🗺️ Level1_DB.asset ➔ 📐 [ 所有者:关卡设计师A ]
│ └─ 关卡1特定的谜题、触发器和事件
│
└── 🗺️ Level2_DB.asset ➔ 📐 [ 所有者:关卡设计师B ]
└─ 关卡2特定的谜题、触发器和事件
结果:
- ✅ 无冲突并行工作
- ✅ 清晰的模块所有权
- ✅ 更快的Git操作(更小的差异)
- ✅ 更容易的代码审查(更小的变更集)
逻辑组织
问题: 一个数据库中有500个事件
- 难以找到特定事件
- 系统之间没有明确的界限
- 难以理解依赖关系
解决方案: 领域驱动的数据库设计
⚔️ Combat_DB ➔ [ 50个事件 ]
└─ 攻击、防御和高频伤害逻辑
🏃 Movement_DB ➔ [ 30个事件 ]
└─ 行走、跳跃、冲刺和基于物理的状态变化
🎒 Inventory_DB ➔ [ 80个事件 ]
└─ 拾取、使用、丢弃和物品耐久度机制
📜 Quest_DB ➔ [ 100个事件 ]
└─ 开始、进度和复杂的完成里程碑
🖥️ UI_DB ➔ [ 70个事件 ]
└─ 菜单转换、HUD更新和对话系统
🔊 Audio_DB ➔ [ 40个事件 ]
└─ 动态音乐层和本地化音效触发器
🗺️ Level_Specific_DB ➔ [ 130个事件 ]
└─ 每个关卡独特的环境和谜题事件
结果:
- ✅ 清晰的概念边界
- ✅ 易于定位相关事件
- ✅ 可理解的依赖关系
- ✅ 模块化测试(仅加载需要的数据库)
运行时注册数据库
公共数据库可以跨关卡保持注册,切换关卡时,把新关卡的数据库资产注册到管理器,旧关卡的数据库不再使用后再取消注册
void SwitchLevelDatabase(GameEventDatabase previousLevelDb, GameEventDatabase currentLevelDb)
{
if (previousLevelDb == currentLevelDb)
return;
var manager = GameEventManager.Instance;
manager.RegisterDatabase(currentLevelDb);
manager.UnregisterDatabase(previousLevelDb);
}
这两个方法调整管理器使用的数据库,按需加载和释放资产可使用 Addressables 数据库
代码架构
位置无关代码
Demo 12的代码与Demo 11完全相同
脚本不知道也不关心事件存在于哪个数据库中:
MultidatabaseRaiser.cs:
[GameEventDropdown]
public GameObjectDamageInfoGameEvent sequenceStartEvent;
public void RequestLaunchA()
{
// 无论此事件在哪个数据库中都有效
// 可以是Core_DB、Combat_DB或任何其他数据库
sequenceStartEvent.Raise(turretA, info);
}
MultidatabaseReceiver.cs:
// 绑定到来自不同数据库的事件的方法
public void OnSystemCheck(GameObject sender, DamageInfo args) // Core_DB
public void OnStartCharging(GameObject sender, DamageInfo args) // Combat_DB
public void OnFireWeapon(GameObject sender, DamageInfo args) // Combat_DB
public void OnCoolDown(GameObject sender, DamageInfo args) // System_DB
public void OnSequenceArchived(GameObject sender, DamageInfo args) // System_DB
发送脚本通过序列化的事件资产引用调用 Raise(),接收方法也无需指定数据库路径
管理器可从已注册、启用的数据库中按事件内部 GUID 查找事件
流程图跨数据库连接
FlowGraph 将三个数据库的事件串成与 Demo 11 相同的五步流程
🧠 [ Core_DB ] ➔ 启动层
│ ├─ 🎬 0_StartSequence ➔ 🔘 根(点火)
│ └─ ⚙️ 1_SystemCheck ➔ 🛡️ 条件(守卫)
│
▼ (信号交接)
│
⚔️ [ Combat_DB ] ➔ 动作层
│ ├─ ⚡ 2_Charge ➔ ⏱️ 延迟(准备)
│ └─ 🔥 3_Fire ➔ 🚀 动作(执行)
│
▼ (信号交接)
│
🛠️ [ System_DB ] ➔ 维护层
│ ├─ ⏳ 4_CoolDown ➔ ⌛ 等待(恢复)
│ └─ 💾 5_Archive ➔ 🧹 过滤(清理)
幕后:
- 每个节点存储事件的GUID
- 管理器在运行时将GUID解析为实际事件
- 即使事件在数据库之间移动,连接仍然有效
- 重组时没有"链接断裂"
事件通过不可变的GUID引用,而不是文件路径
您可以:
- 在数据库之间移动事件
- 重命名数据库文件
- 重组文件夹结构
只要事件的GUID不改变,所有引用都保持有效
最佳实践
何时创建多个数据库
好理由:
- ✅ 团队所有权 - 不同团队在不同系统上工作
- ✅ 逻辑域 - 清晰的概念边界(战斗、UI、音频)
- ✅ 动态加载 - 按关卡或模式加载/卸载事件
- ✅ 版本控制 - 减少合并冲突
- ✅ 测试 - 仅为特定测试加载相关数据库
坏理由:
- ❌ 性能 - 多数据库没有运行时成本,所以不要为了速度而拆分
- ❌ 事件数量 - 一个数据库中50个事件没问题,不要过度拆分
- ❌ 过早优化 - 从一个数据库开始,当感到痛苦时再拆分
推荐的数据库结构
小型项目(< 100个事件):
📂 Databases/
└─ 🧠 GameEventDatabase_Main.asset ➔ [ 📦 一体化 ]
└─ (所有战斗、UI和系统事件都在这里)
中型项目(100-300个事件):
📂 Databases/
├─ 🧠 Core_DB.asset ➔ [ ⚙️ 基础系统 ]
├─ 🎮 Gameplay_DB.asset ➔ [ ⚔️ 主要机制 ]
└─ 🖥️ UI_DB.asset ➔ [ 🎨 菜单和HUD ]
大型项目(300+个事件):
📂 Databases/
├─ 🧠 Core_DB.asset ➔ 💻 [ 全局系统 ]
├─ ⚔️ Combat_DB.asset ➔ 🤺 [ 战斗机制 ]
├─ 🏃 Movement_DB.asset ➔ 🤸 [ 角色运动 ]
├─ 🎒 Inventory_DB.asset ➔ 📦 [ 物品和网格管理 ]
├─ 📜 Quest_DB.asset ➔ 📖 [ 任务和故事逻辑 ]
├─ 🖥️ UI_DB.asset ➔ 🎨 [ 全局界面 ]
├─ 🔊 Audio_DB.asset ➔ 🎧 [ 动态音景 ]
│
└─ 🗺️ Level_Specific/ ➔ 📐 [ 每个关卡的独特事件 ]
├─ Level_01_DB.asset
├─ Level_02_DB.asset
└─ ...
命名约定
数据库文件:
GameEventDatabase_[模块].asset(编辑器工具所需的前缀)- 示例:
GameEventDatabase_Combat.asset、GameEventDatabase_UI.asset
事件名称:
- 步骤/优先级前缀:
0_StartSequence、1_SystemCheck - 或模块前缀:
Combat_AttackStart、UI_MenuOpen - 避免通用名称:
Event1、MyEvent(难以搜索)