Game lifecycle
A MicroEngine game is a subclass of Game that declares its settings, builds its content once in Load and
pushes a first scene. A launcher pairs it with a platform and runs a fixed-step loop: input is sampled and the
simulation advanced in exact steps of 1 / UpdateRate seconds (60 Hz by default), then the frame is rendered once. This page describes that contract;
the step-by-step tutorial is in Making a game.
The Game class
| Member | Purpose |
|---|---|
Settings (abstract) |
GameSettings: title, virtual canvas size, window scale, update rate, background colour |
Load() (abstract) |
Runs once after the platform exists: build atlases, sounds, music, push the first scene |
Unload() |
Runs once before the platform is disposed: release textures you created |
CreateInput() |
Returns the InputMap; default is InputMap.CreateDefault() (see Input) |
Graphics, Audio, Input, Scenes, Font |
Services available from Load on |
Time, UpdateCount |
Simulated seconds and fixed updates since start |
Random |
Per-run Random, seeded by the launcher (RunOptions.Seed) |
Width, Height |
The virtual canvas size from Settings |
Quit() / QuitRequested |
Asks the loop to stop after the current frame |
UseAchievements(...), Achievements |
Optional achievements (see Achievements) |
public abstract GameSettings Settings { get; }public IGraphicsDevice Graphics => Platform.Graphics;public AudioMixer Audio { get; private set; } = null!;public InputMap Input { get; private set; } = null!;public SceneManager Scenes { get; private set; } = null!;public BitmapFont Font { get; private set; } = null!;// .../// <summary>Called once after the platform exists. Create atlases, sounds and push the first scene.</summary>protected abstract void Load();/// <summary>Called once before the platform is disposed.</summary>protected virtual void Unload() { }/// <summary>Override to change bindings; the default suits most games.</summary>protected virtual InputMap CreateInput() => InputMap.CreateDefault();Source: src/MicroEngine/Core/Game.cs
GameSettings
public sealed record GameSettings(string Title, int VirtualWidth = 320, int VirtualHeight = 180, int Scale = 4){ /// <summary>Fixed simulation rate in updates per second.</summary> public int UpdateRate { get; init; } = 60; public Color Background { get; init; } = Color.Black;}Source: src/MicroEngine/Core/Platform.cs
VirtualWidth/VirtualHeight define the pixel-art canvas every scene draws into (Rendering);
Scale is only the initial window size on desktop. Neon Rift uses a larger canvas:
public override GameSettings Settings { get; } = new("Neon Rift", 480, 270, 3) { Background = Color.Hex("0d0b1a") };Source: games/NeonRift/RiftGame.cs
Start-up order
Game.Initialize (called by the loop) sets services up in a fixed order, then runs Load and applies the scene
changes Load requested:
internal void Initialize(IPlatform platform, int seed){ _platform = platform; Random = new Random(seed); Input = CreateInput(); Audio = new AudioMixer(platform.Audio); Font = BitmapFont.CreateDefault(platform.Graphics); Scenes = new SceneManager(this); Load(); Scenes.ApplyPending();}Source: src/MicroEngine/Core/Game.cs
Build every texture, sound and song in Load, never per frame: textures need the platform’s graphics device,
and synthesis is too slow to repeat each update.
Loop, runner and session
| Type | Role |
|---|---|
GameLoop |
One frame per Tick(). Captures errors instead of throwing; Finish() shuts the game down once and returns a RunResult |
GameRunner.Run(game, platform, options) |
Ticks a GameLoop in a blocking loop; used by desktop and headless runs |
GameSession |
For hosts that drive frames themselves and outlive a run (a browser page): Start, Tick, Stop, Result |
RunOptions |
Script, CaptureDirectory, MaxUpdates, Seed, Mute |
RunResult |
Updates, Frames, Captures, Error |
public static RunResult Run(Game game, IPlatform platform, RunOptions? options = null){ var loop = new GameLoop(game, platform, options); if (loop.Start()) while (loop.Tick()) { } return loop.Finish();}Source: src/MicroEngine/Core/GameRunner.cs
GameSession builds a fresh game and platform on every Start, and when a run ends (quit, error or Stop)
it calls Finish and disposes the platform, so the host can show its own screen and start again. See
Backends for how the desktop and browser launchers use these types.
Fixed step and frame order
Each Tick runs one frame:
platform.PollEvents().- Accumulate time. Real-time platforms add wall-clock time (capped at 0.25 s per frame); headless and fixed-step platforms add exactly one step.
- Run fixed steps of
1 / UpdateRateseconds, at most five per frame. Each step applies the input script, samples devices into theInputMap, thenGame.Stepupdates scenes, the audio mixer and achievement banners, flushes achievement progress about once a second, and advances input edge detection. - Render once: clear to
Settings.Background, draw the scene stack and any transition, draw achievement banners. - If the script asked for a capture at this update, save a PNG of the virtual frame.
while (_accumulator >= _step - 1e-9 && steps < 5){ script?.Apply(_game.UpdateCount, _game.Input); _game.Input.Sample(_platform.Input); _game.Step((float)_step); _accumulator -= _step; steps++; // ...}if (steps == 5) _accumulator = Math.Min(_accumulator, _step);Source: src/MicroEngine/Core/GameRunner.cs
When the machine falls behind, the cap drops the backlog instead of spiralling, so the game slows down rather than freezing. Because simulation only ever advances in whole steps, scripted runs on headless or fixed-step platforms are deterministic (ADR 0003).
Quitting and shutdown
A run ends when Game.Quit() was called, the platform reports CloseRequested (the desktop window was closed),
RunOptions.MaxUpdates was reached, an input script’s quit update arrived, or an exception escaped. In every
case GameLoop.Finish calls Game.Shutdown:
internal void Shutdown(){ Scenes.Clear(); _achievements?.Flush(); Unload(); Font.Dispose();}Source: src/MicroEngine/Core/Game.cs
Scenes.Clear() pops every scene (so each one’s Exit runs), pending achievement progress is written, then
your Unload runs. Errors are reported in RunResult.Error; DesktopLauncher.Run turns them into exit code 1.
Suspension
A phone sends an app to the background when the player switches away or takes a call; the platform then reports
Suspended (Backends). The loop reacts once on each change:
- Going away: music pauses, pending achievement progress is written, then
OnSuspended()runs. No update and no frame happens while suspended. - Coming back: music resumes, the clock restarts (the time away is not simulated), then
OnResumed()runs before the next update.
protected override void OnSuspended() => Scenes.Push(new PauseScene(this)); // for exampleBoth hooks are optional; on desktops and in browsers they never run. Save anything the player would hate to lose in
OnSuspended: a phone may end a background app without warning. Source:
src/MicroEngine/Core/GameRunner.cs
Testing hooks
InitializeForTest(platform, seed) and StepForTest() initialise a game and advance single steps without the
loop, which suits unit tests over a HeadlessPlatform (see Backends).
Related
- Scenes: what runs inside
Scenes.UpdateandScenes.Draw - Architecture: layers and module map
- ADR 0003: why a scene stack and a fixed-step runner