Skip to main content

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

  1. Core Runtime has zero external dependencies on Online or Importer modules.
  2. Online features are opt-in via the MCE_ONLINE scripting define symbol.
  3. Editor code is strictly separated from Runtime via .asmdef boundaries.
  4. 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:

AssemblyLocationPurposeReferences
OpenMon.MCE.RuntimeCore/Runtime/Core game systemsFoundation libs only
OpenMon.MCE.EditorCore/Editor/Editor toolingRuntime + UnityEditor
MCE.Online.RuntimeMCE_Online/Runtime/Nakama networkingRuntime + Nakama SDK
MCE.Online.EditorMCE_Online/Editor/Online editor toolsOnline Runtime + UnityEditor
MCE.EssentialsImporter.EditorMCE_EssentialsImporter/Editor/Import pipelineRuntime + KaitaiStruct
MCE.Runtime.TestsCore/Tests/Runtime/Core testsRuntime + 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

InstallerBinds
GameFlowInstallerCore game flow services
PlayerCharacterInstallerPlayerCharacter, character services
BattleLauncherInstallerBattleLauncher, battle entry point
GridInstallerGridController, tile data
SceneInfoInstallerSceneInfo, scene metadata
WorldDatabaseInstallerWorldDatabase, global map data
TileDataInstallerTile type data
GlobalGridManagerInstallerCross-map navigation
SavegameInstallerSavegameManager, serialization
EvolutionManagerInstallerEvolutionManager

Conventions

  • Bindings are .AsSingle().Lazy() by convention (single instance, created on first use).
  • [Inject] is used on Construct() methods, not constructors.
  • DialogManager.Factory pattern 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] private over public fields.
  • Separate Runtime/ and Editor/ behind .asmdef boundaries.
  • Conventional Commits: feat:, fix:, docs:, refactor:.
Serialized Fields

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

  1. Create your classes in Runtime/YourSystem/.
  2. Create an installer for dependency injection.
  3. Add the installer to the appropriate scene context or project context.
  4. If it needs save data, extend SavableObject.
  5. If it has editor tooling, create classes in Editor/YourSystem/.

Modifying an Existing System

  1. Read the existing code and understand the module's dependencies.
  2. Check if the change can be done via configuration or extension rather than modification.
  3. If modifying, follow the existing patterns in the module.
  4. Update tests to cover the change.
  5. 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.