Architecture Overview
This guide is for Source tier users who have access to the full MCE C# source code.
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/ # AudioManager, BGM/SE/ME
Localization/ # Multi-language support
Badges/ # Badge tracking
Input/ # Input abstraction
MonsterDex/ # Dex tracking (seen/caught)
Animation/ # Animation utilities
Shaders/ # Shader source files
Editor/ # Editor-only tooling
ArtGenerator/ # AI sprite generation
MonsterCreator/ # Monster creation wizard
Tools/ # Database Browser, Type Chart Editor, validators
Followers/ # Follower spritesheet tools
Actors/ # CommandGraph editor
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 DLL-tier users.
Assembly Structure
MCE uses Unity Assembly Definitions to enforce compilation boundaries:
| Assembly | Location | Purpose | References |
|---|---|---|---|
OpenMon.MCE.Runtime | Core/Runtime/ | Core game systems | Foundation libs only |
OpenMon.MCE.Editor | Core/Editor/ | Editor tooling | Runtime + UnityEditor |
MCE.Online.Runtime | MCE_Online/Runtime/ | Nakama networking | Runtime + Nakama SDK |
MCE.Online.Editor | MCE_Online/Editor/ | Online editor tools | Online Runtime + UnityEditor |
MCE.EssentialsImporter.Editor | MCE_EssentialsImporter/Editor/ | Import pipeline | Runtime + KaitaiStruct |
MCE.Runtime.Tests | Core/Tests/Runtime/ | Core tests | Runtime + 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:
public class BattleLauncherInstaller : MonoInstaller
{
[SerializeField] private BattleLauncher battleLauncherPrefab;
public override void InstallBindings()
{
Container.Bind<IBattleLauncher>()
.To<BattleLauncher>()
.FromComponentInNewPrefab(battleLauncherPrefab)
.AsSingle()
.Lazy();
}
}
Key Installers
| Installer | Binds |
|---|---|
GameFlowInstaller | Core game flow services |
PlayerCharacterInstaller | PlayerCharacter, character services |
BattleLauncherInstaller | BattleLauncher, battle entry point |
GridInstaller | GridController, tile data |
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 handle per-scene services.
- Project-scoped installers (in
ProjectContext) handle global services.
Injection Points
public class SomeManager : MonoBehaviour
{
// Field injection (for MonoBehaviours)
[Inject] private IMonsterDatabase database;
// Method injection (preferred for explicit dependencies)
[Inject]
public void Construct(IBattleSystem battle, IPlayerData player)
{
this.battle = battle;
this.player = player;
}
}
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
├── DataByFormEntry[] (per-form overrides)
├── 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 are O(1) by dex number, O(n) by name. Cache name lookups if called frequently.
- Battle modules are initialized once per battle, not per turn. Module creation is not a hot path.
- Tilemaps have a performance ceiling around 200x200 tiles. Split larger maps.
- 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.