Architecture Overview
This guide is for Source and Studio edition users who have access to the full MCE C# source code.
The arrows only point one way on purpose. Core never depends on Online or on the importer,
which is what lets the online layer be genuinely optional instead of optional-in-the-README.
This document covers the module hierarchy, assembly structure, dependency injection patterns, and key runtime systems that make up the Monster Capture Engine.
Module Hierarchy
MCE is organized into modules that follow a layered architecture:
Assets/
OpenMon/
Core/
Runtime/ # Core engine (REQUIRED)
Initialization/ # Engine startup, first-time wizard
Monster/ # MonsterInstance, Roster, evolution, breeding
MonsterDatabase/ # ScriptableObject definitions (species, moves, items, abilities)
Battle/ # BattleManager, 15 modules, state machine, AI
Characters/ # MCECharacterController, PlayerCharacter, followers
Actors/ # Actor system, CommandGraph visual scripting
World/ # GridController, GlobalGridManager, tiles, encounters
Saves/ # SavegameManager, SavableObject, GameVariables
Player/ # GlobalGameData, player state
UI/ # All UI controllers and views
Quests/ # Quest system (objectives, rewards, tracking)
SDK/ # Public API interfaces
GameFlow/ # Game state management
Configuration/ # ScriptableObject configs
Rendering/ # URP shaders, time-of-day, weather
Audio/ # AudioSettingsManager, AudioConfiguration (the playback engine is in Foundation)
Localization/ # Multi-language support
Badges/ # Badge tracking
Input/ # Input abstraction
MonsterDex/ # Dex tracking (seen/caught)
Animation/ # Animation utilities
WorldGenerator/ # Procedural region generator
Licensing/ # Tier and license checks
Shaders/ # Shader source files
Editor/ # Editor-only tooling
MonsterCreator/ # Monster creation wizard
Tools/ # Database Browser, Type Chart Editor, validators
Followers/ # Follower spritesheet tools
Actors/ # CommandGraph editor
Hub/ # The OpenMon Hub window
MapEditor/ # Scene View map painting overlays
Callbacks/ # Editor callback utilities
Dependency Rules
- Core Runtime has zero external dependencies on Online or Importer modules.
- Online features are opt-in via the
MCE_ONLINEscripting define symbol. - Editor code is strictly separated from Runtime via
.asmdefboundaries. - The SDK interfaces in
Runtime/SDK/are the public API surface for users of the compiled (DLL) editions.
Assembly Structure
MCE uses Unity Assembly Definitions to enforce compilation boundaries:
| Assembly | Location | Purpose | References |
|---|---|---|---|
OpenMon.Foundation.Runtime | Foundation/Runtime/ | Shared framework: DI, audio, localization, scene management, tweening | Zenject and Unity packages |
OpenMon.Foundation.Editor | Foundation/Editor/ | Foundation editor tooling | Foundation Runtime + UnityEditor |
OpenMon.MCE.Runtime | Core/Runtime/ | Core game systems | Foundation libs only |
OpenMon.MCE.Editor | Core/Editor/ | Editor tooling | Runtime + UnityEditor |
OpenMon.Hub.Editor | Core/Editor/Hub/ | The OpenMon Hub window (it calls importer and online tools by name, so it depends on neither) | Foundation Runtime |
MCE.Online.Runtime | Online/Runtime/ | Nakama networking | Runtime + Nakama SDK |
MCE.Online.Editor | Online/Editor/ | Online editor tools | Online Runtime + UnityEditor |
MCE.EssentialsImporter.Runtime | Importer/Runtime/ | PBS/rxdata parsers and the import validator | Runtime + KaitaiStruct |
MCE.EssentialsImporter.Editor | Importer/Editor/ | Import pipeline | Runtime + KaitaiStruct |
OpenMon.Setup | Setup/ | Package dependency installer and quick setup | none (isolated on purpose) |
MCE.Runtime.Tests | Core/Tests/Runtime/ | Core play-mode tests | Runtime + NUnit |
MCE.Editor.Tests | Core/Tests/Editor/ | Core edit-mode tests | Runtime + Editor + NUnit |
Why Assembly Definitions Matter
- Compile isolation: Changes to Editor code do not recompile Runtime.
- Dependency enforcement: The compiler prevents Runtime from referencing Editor APIs.
- Build stripping: Editor assemblies are excluded from player builds.
- Test isolation: Test assemblies can reference internals without exposing them in production.
Dependency Injection (Zenject)
MCE uses Zenject (Extenject) with the installer pattern for dependency injection.
Installer Pattern
Each subsystem has its own installer that registers bindings. Most are ScriptableObject installers; the battle launcher's is typical:
[CreateAssetMenu(menuName = "MCE/Dependency Injection/Battle Launcher Installer",
fileName = "BattleLauncherInstaller")]
public class BattleLauncherInstaller : LazySingletonScriptableInstaller<BattleLauncher>
{
public override void InstallBindings()
{
Reference.Reset();
Container.QueueForInject(Reference);
Container.Bind<IBattleLauncher>().FromInstance(Reference).AsSingle().Lazy();
}
}
BattleLauncher is itself a ScriptableObject (an asset), and the installer asset points at it. The Dependency Injection page lists every installer.
Key Installers
| Installer | Binds |
|---|---|
GameFlowInstaller | Core game flow services |
PlayerCharacterInstaller | PlayerCharacter, character services |
BattleLauncherInstaller | IBattleLauncher, the battle entry point |
GridInstaller | The scene's grid (a MonoInstaller) |
SceneInfoInstaller | SceneInfo, scene metadata |
WorldDatabaseInstaller | WorldDatabase, global map data |
TileDataInstaller | Tile type data |
GlobalGridManagerInstaller | Cross-map navigation |
SavegameInstaller | SavegameManager, serialization |
EvolutionManagerInstaller | EvolutionManager |
Conventions
- Bindings are
.AsSingle().Lazy()by convention (single instance, created on first use). [Inject]is used onConstruct()methods, not constructors.DialogManager.Factorypattern is used for runtime UI instantiation.- Scene-scoped installers (for example
GridInstaller, aMonoInstaller) handle per-scene services. - Project-scoped installers (ScriptableObject assets on the
ProjectContextprefab) handle global services.
Injection Points
public class SomeManager : MonoBehaviour
{
// Field injection (for MonoBehaviours).
// Note: of the SDK interfaces, only IMonsterCaptureEngine is bound —
// take Database/Battle/Player/SaveSystem from its properties.
[Inject] private IMonsterCaptureEngine engine;
private IBattleSystem battle;
private void Awake()
{
battle = engine.Battle;
}
// Method injection (preferred for explicit dependencies on engine-internal types)
[Inject]
public void Construct(MCESettings settings, MonsterDatabaseInstance database)
{
this.settings = settings;
this.database = database;
}
}
Key Runtime Systems
MCEInitialization Flow
MCEInitialization is the first thing that runs when the game starts:
1. Screen setup (resolution, orientation)
2. Load databases (monsters, moves, items, abilities)
3. Download localization data (if remote)
4. Run first-time wizard (if needed)
5. Initialize subsystems (save manager, audio, input)
6. Signal ready
7. Load main menu or continue saved game
This is a MonoBehaviour on a persistent GameObject that survives scene loads.
Monster System
The monster data model:
MonsterEntry (ScriptableObject - species definition)
├ ── Base stats, types, abilities
├── List<DataByFormEntry> (private per-form overrides)
├── DataByFormEntry.Evolutions: List<EvolutionData> (evolution paths)
└── Move lists (level-up, TM, egg, tutor)
MonsterInstance (runtime - individual monster)
├── Species reference (MonsterEntry)
├── Level, XP
├── IVs[6], EVs[6]
├── Nature
├── Current HP, Status
├── MoveSlot[4] (with PP)
├── Friendship, OT data
└── Form index
Roster (party of up to 6 MonsterInstances)
MonsterStorage (PC boxes)
Battle System
See Battle Internals for the deep dive.
World System
GlobalGridManager (cross-map navigation)
└── GridController (per-scene grid)
├── TileData grid (collision, type per tile)
├── EncounterTile zones
└── SceneInfo (metadata)
MCECharacterController (movement)
├── PlayerCharacter (input + encounters)
└── ActorCharacter (NPC AI movement)
Save System
SavegameManager
├── MCESavesSerializer (JSON read/write)
├── SavableObject[] (registered savables)
└── GameVariables (global state)
Code Style
The codebase follows these conventions:
- 4-space indentation, braces on new lines.
- PascalCase for public types, methods, properties.
- camelCase for private fields and locals.
[SerializeField] privateover public fields.- Separate
Runtime/andEditor/behind.asmdefboundaries. - Conventional Commits:
feat:,fix:,docs:,refactor:.
Never rename serialized fields without a migration plan. Scenes and prefabs depend on serialized field names. Use [FormerlySerializedAs("oldName")] for renames.
Extending the Engine
Adding a New System
- Create your classes in
Runtime/YourSystem/. - Create an installer for dependency injection.
- Add the installer to the appropriate scene context or project context.
- If it needs save data, extend
SavableObject. - If it has editor tooling, create classes in
Editor/YourSystem/.
Modifying an Existing System
- Read the existing code and understand the module's dependencies.
- Check if the change can be done via configuration or extension rather than modification.
- If modifying, follow the existing patterns in the module.
- Update tests to cover the change.
- Document the change in commit messages.
Performance Considerations
- Database lookups through the SDK (
GetMonster(dexNumber),GetMonster(name)) scan the list of species. Cache the entry if you call them often. - Battle modules are components of the battle GameObject, created with it once per battle, not per turn.
- Large tilemaps cost more per step and per frame. Split very large maps into connected scenes (see Neighbours in Grid-Based Movement).
- Save serialization is synchronous on the main thread. Keep save data sizes reasonable.
- CommandGraph execution is coroutine-based. Complex graphs with many nodes are fine for event scripting but should not be used for per-frame logic.