跳到主要内容

架构概述

本指南适用于拥有完整 MCE C# 源代码访问权限的 Source 版本用户。

本文档涵盖模块层级结构、程序集结构、依赖注入模式以及构成 Monster Capture Engine 的关键运行时系统。

模块层级结构

MCE 按照分层架构组织模块:

Assets/
OpenMon/
Core/
Runtime/ # 核心引擎(必需)
Initialization/ # 引擎启动、首次向导
Monster/ # MonsterInstance、Roster、进化、繁殖
MonsterDatabase/ # ScriptableObject 定义(物种、招式、道具、特性)
Battle/ # BattleManager、15+ 模块、状态机、AI
Characters/ # MCECharacterController、PlayerCharacter、跟随者
Actors/ # Actor 系统、CommandGraph 可视化脚本
World/ # GridController、GlobalGridManager、地块、遭遇
Saves/ # SavegameManager、SavableObject、GameVariables
Player/ # GlobalGameData、玩家状态
UI/ # 所有 UI 控制器和视图
Quests/ # 任务系统(目标、奖励、追踪)
SDK/ # 公共 API 接口
GameFlow/ # 游戏状态管理
Configuration/ # ScriptableObject 配置
Rendering/ # URP 着色器、昼夜系统、天气
Audio/ # AudioManager、BGM/SE/ME
Localization/ # 多语言支持
Badges/ # 徽章追踪
Input/ # 输入抽象
MonsterDex/ # 图鉴追踪(已见/已捕获)
Animation/ # 动画工具
Shaders/ # 着色器源文件
Editor/ # 仅编辑器工具
ArtGenerator/ # AI 精灵图生成
MonsterCreator/ # 怪兽创建向导
Tools/ # 数据库浏览器、属性表编辑器、验证器
Followers/ # 跟随者精灵表工具
Actors/ # CommandGraph 编辑器
Callbacks/ # 编辑器回调工具

依赖规则

  1. 核心运行时对 Online 或 Importer 模块零外部依赖
  2. 在线功能通过 MCE_ONLINE 脚本定义符号可选启用。
  3. Editor 代码通过 .asmdef 边界严格与 Runtime 分离。
  4. Runtime/SDK/ 中的 SDK 接口是 DLL 版本用户的公共 API 表面。

程序集结构

MCE 使用 Unity 程序集定义来强制编译边界:

程序集位置用途引用
OpenMon.MCE.RuntimeCore/Runtime/核心游戏系统仅基础库
OpenMon.MCE.EditorCore/Editor/编辑器工具Runtime + UnityEditor
MCE.Online.RuntimeMCE_Online/Runtime/Nakama 网络Runtime + Nakama SDK
MCE.Online.EditorMCE_Online/Editor/在线编辑器工具Online Runtime + UnityEditor
MCE.EssentialsImporter.EditorMCE_EssentialsImporter/Editor/导入管线Runtime + KaitaiStruct
MCE.Runtime.TestsCore/Tests/Runtime/核心测试Runtime + NUnit

为什么程序集定义很重要

  • 编译隔离:Editor 代码的更改不会重新编译 Runtime。
  • 依赖强制:编译器阻止 Runtime 引用 Editor API。
  • 构建裁剪:Editor 程序集在玩家构建中被排除。
  • 测试隔离:测试程序集可以引用内部成员而不在生产中暴露。

依赖注入(Zenject)

MCE 使用 Zenject(Extenject)配合安装器模式进行依赖注入。

安装器模式

每个子系统有自己的安装器来注册绑定:

public class BattleLauncherInstaller : MonoInstaller
{
[SerializeField] private BattleLauncher battleLauncherPrefab;

public override void InstallBindings()
{
Container.Bind<IBattleLauncher>()
.To<BattleLauncher>()
.FromComponentInNewPrefab(battleLauncherPrefab)
.AsSingle()
.Lazy();
}
}

关键安装器

安装器绑定内容
GameFlowInstaller核心游戏流程服务
PlayerCharacterInstallerPlayerCharacter、角色服务
BattleLauncherInstallerBattleLauncher、战斗入口
GridInstallerGridController、地块数据
SceneInfoInstallerSceneInfo、场景元数据
WorldDatabaseInstallerWorldDatabase、全局地图数据
TileDataInstaller地块类型数据
GlobalGridManagerInstaller跨地图导航
SavegameInstallerSavegameManager、序列化
EvolutionManagerInstallerEvolutionManager

约定

  • 绑定按约定使用 .AsSingle().Lazy()(单实例,首次使用时创建)。
  • [Inject] 用于 Construct() 方法,而非构造函数。
  • DialogManager.Factory 模式用于运行时 UI 实例化。
  • 场景作用域安装器处理每场景服务。
  • 项目作用域安装器(在 ProjectContext 中)处理全局服务。

注入点

public class SomeManager : MonoBehaviour
{
// 字段注入(用于 MonoBehaviour)
[Inject] private IMonsterDatabase database;

// 方法注入(推荐用于显式依赖)
[Inject]
public void Construct(IBattleSystem battle, IPlayerData player)
{
this.battle = battle;
this.player = player;
}
}

关键运行时系统

MCEInitialization 流程

MCEInitialization 是游戏启动时首先运行的内容:

1. 屏幕设置(分辨率、方向)
2. 加载数据库(怪兽、招式、道具、特性)
3. 下载本地化数据(如果是远程的)
4. 运行首次向导(如果需要)
5. 初始化子系统(存档管理器、音频、输入)
6. 发出就绪信号
7. 加载主菜单或继续存档游戏

这是持久化 GameObject 上的一个 MonoBehaviour,在场景加载时不被销毁。

怪兽系统

怪兽数据模型:

MonsterEntry(ScriptableObject - 物种定义)
├── 基础能力值、属性、特性
├── DataByFormEntry[](每形态覆盖)
├── EvolutionData[](进化路径)
└── 招式列表(升级、TM、蛋、教授)

MonsterInstance(运行时 - 个体怪兽)
├── 物种引用(MonsterEntry)
├── 等级、经验值
├── IVs[6]、EVs[6]
├── 性格
├── 当前 HP、状态
├── MoveSlot[4](含 PP)
├── 亲密度、初训数据
└── 形态索引

Roster(最多 6 只 MonsterInstance 的队伍)
MonsterStorage(PC 盒子)

战斗系统

请参阅战斗内部机制获取详细解析。

世界系统

GlobalGridManager(跨地图导航)
└── GridController(每场景网格)
├── TileData 网格(碰撞、每地块类型)
├── EncounterTile 区域
└── SceneInfo(元数据)

MCECharacterController(移动)
├── PlayerCharacter(输入 + 遭遇)
└── ActorCharacter(NPC AI 移动)

存档系统

SavegameManager
├── MCESavesSerializer(JSON 读写)
├── SavableObject[](已注册的可存档对象)
└── GameVariables(全局状态)

代码风格

代码库遵循以下约定:

  • 4 空格缩进,大括号另起一行。
  • PascalCase 用于公共类型、方法、属性。
  • camelCase 用于私有字段和局部变量。
  • [SerializeField] private 优先于公共字段。
  • Runtime/Editor/ 通过 .asmdef 边界分离。
  • 约定式提交:feat:fix:docs:refactor:
序列化字段

不要在没有迁移计划的情况下重命名序列化字段。场景和预制体依赖于序列化字段名称。重命名时使用 [FormerlySerializedAs("oldName")]

扩展引擎

添加新系统

  1. Runtime/YourSystem/ 中创建你的类。
  2. 为依赖注入创建安装器。
  3. 将安装器添加到适当的场景上下文或项目上下文。
  4. 如果需要存档数据,扩展 SavableObject
  5. 如果有编辑器工具,在 Editor/YourSystem/ 中创建类。

修改现有系统

  1. 阅读现有代码并理解模块的依赖关系。
  2. 检查更改是否可以通过配置或扩展完成,而非修改。
  3. 如果要修改,遵循模块中的现有模式。
  4. 更新测试以覆盖更改。
  5. 在提交消息中记录更改。

性能注意事项

  • 数据库查找按图鉴编号为 O(1),按名称为 O(n)。如果频繁调用,请缓存名称查找。
  • 战斗模块每场战斗初始化一次,而非每回合。模块创建不是热路径。
  • Tilemap 在约 200x200 地块时有性能上限。拆分更大的地图。
  • 存档序列化在主线程上同步执行。保持存档数据大小合理。
  • CommandGraph 执行基于协程。包含许多节点的复杂图表适合事件脚本,但不应用于每帧逻辑。