Contracts
This page lists the public types a game and a backend depend on, with their real signatures from src/. They are
the stable surface the four games are written against: the game base class and its settings, scenes, the
platform boundary and its device interfaces, save storage, input actions and achievements. Bodies are omitted
with // ...; each block links its source file.
Game
src/MicroEngine/Core/Game.cs, namespace MicroEngine.Core.
public abstract class Game{ 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!; public float Time { get; private set; } public long UpdateCount { get; private set; } public Random Random { get; private set; } = new(1); public bool QuitRequested { get; private set; } public int Width => Settings.VirtualWidth; public int Height => Settings.VirtualHeight; public AchievementTracker Achievements => // ... public bool HasAchievements => _achievements is not null; public AchievementToasts? AchievementToasts { get; private set; } protected IPlatform Platform => // ...
protected abstract void Load(); protected virtual void Unload() { } protected virtual InputMap CreateInput() => InputMap.CreateDefault(); public void Quit() => QuitRequested = true; protected AchievementTracker UseAchievements(IEnumerable<Achievement> catalog, SaveStore store, UiTheme? theme = null) public void InitializeForTest(IPlatform platform, int seed = 1) => Initialize(platform, seed); public void StepForTest()}| Member | Contract |
|---|---|
Load |
Called once after the platform exists. Build atlases and sounds, push the first scene |
Unload |
Called once before the platform is disposed |
CreateInput |
Override to change or add action bindings |
Random |
Deterministic per run; seeded by the launcher (seed 1 under a script) |
Achievements |
Throws until UseAchievements was called |
InitializeForTest / StepForTest |
Run Load, then advance one fixed update after sampling input, without a loop |
GameSettings
src/MicroEngine/Core/Platform.cs.
public sealed record GameSettings(string Title, int VirtualWidth = 320, int VirtualHeight = 180, int Scale = 4){ public int UpdateRate { get; init; } = 60; public Color Background { get; init; } = Color.Black;}All options are described in Configuration.
Scene and SceneManager
src/MicroEngine/Scenes/Scene.cs, namespace MicroEngine.Scenes.
public abstract class Scene{ public Game Game => // ... public InputMap Input => Game.Input; public AudioMixer Audio => Game.Audio; public BitmapFont Font => Game.Font; public CoroutineRunner Coroutines { get; } = new(); public Tweener Tweens { get; } = new(); public float Age { get; private set; } public virtual bool IsOverlay => false; public virtual bool BlocksUpdateBelow => true;
public virtual void Enter() { } public virtual void Exit() { } public virtual void Resume() { } public abstract void Update(float dt); public abstract void Draw(IRenderer renderer);}
public sealed class SceneManager{ public Scene? Current => // ... public int Count => _stack.Count; public bool InTransition => _transition is not null; public IReadOnlyList<Scene> Stack => _stack; public void Push(Scene scene, Transition? transition = null) public void Pop(Transition? transition = null) public void Replace(Scene scene, Transition? transition = null) public void Reset(Scene scene, Transition? transition = null)}Stack changes are scheduled and applied between updates. Transition.Fade, Iris, Diamonds and Wipe create
the built-in transitions. See Scenes.
IPlatform
src/MicroEngine/Core/Platform.cs. Everything a backend provides.
public interface IPlatform : IDisposable{ IGraphicsDevice Graphics { get; } IAudioDevice Audio { get; } IInputSource Input { get; } bool CloseRequested { get; } double Now { get; } bool RealTime { get; } bool Suspended => false; void PollEvents(); IRenderer BeginFrame(); void EndFrame(); void SaveScreenshot(string path); void ToggleFullscreen();}RealTime is true when time follows the wall clock (desktop, browser) and false when the loop advances one step
per frame (headless). Suspended is true while the system has taken the app away (a phone app in the background);
the loop then pauses. Implementations: SdlPlatform, RaylibPlatform, HeadlessPlatform.
Platform profiles
src/MicroEngine/Core/PlatformProfiles.cs. How a host chooses and
assembles a platform (Backends, ADR 0016).
| Type | Role |
|---|---|
PlatformProfile(name, description, create) |
A named way to build an IPlatform; CheckSupport says why it cannot run here; Replaceable lists the PlatformParts a host may supply |
PlatformCatalog |
Profiles in order of preference; Default is the first that can run, Resolve(name) finds one or throws |
PlatformRequest(settings) |
Window options (Scale, Hidden, FixedStep, Mute, CaptureScale), an optional Audio factory and ExtraInput sources; OpensOwnAudio tells a backend whether to open its device |
PlatformConfigurationException |
An unknown or unavailable profile, or a replacement the profile does not allow |
CompositeInput |
Several input sources read as one |
GameLauncher.Run(game, args, catalog, configure) |
The desktop command line over any catalog |
Device interfaces
src/MicroEngine/Graphics/IGraphics.cs:
public interface IRenderer{ int Width { get; } int Height { get; } void Clear(Color color); void Draw(Texture texture, RectF source, Vector2 position, Color tint, in DrawOptions options = default); void DrawStretched(Texture texture, RectF source, RectF destination, Color tint); void FillRect(RectF rect, Color color); void FillCircle(Vector2 center, float radius, Color color); void FillTriangle(Vector2 a, Vector2 b, Vector2 c, Color color); void DrawLine(Vector2 from, Vector2 to, float thickness, Color color); void PushCamera(Camera2D camera); void PopCamera(); void SetBlendMode(BlendMode mode); void SetClip(RectF? screenRect);}
public interface IGraphicsDevice{ Texture CreateTexture(PixelImage image);}src/MicroEngine/Audio/Audio.cs:
public interface IAudioDevice{ bool IsAvailable { get; } SoundHandle LoadSound(string name, AudioClip clip); MusicHandle LoadMusic(string name, AudioClip clip, bool loop = true); MusicHandle LoadMusicStream(string name, byte[] encoded, string format, bool loop = true); void Play(SoundHandle sound, float volume = 1f, float pitch = 1f, float pan = 0.5f); void PlayMusic(MusicHandle music, float volume = 1f); void StopMusic(); void SetMusicVolume(float volume); void PauseMusic(bool paused); MusicHandle? CurrentMusic { get; } void Update();}src/MicroEngine/Input/Input.cs:
public interface IInputSource{ bool IsKeyDown(Key key); bool IsPadDown(PadButton button); System.Numerics.Vector2 LeftStick { get; }}Games rarely touch these directly: they use AudioMixer, InputMap and the renderer passed to Draw.
InputMap
src/MicroEngine/Input/Input.cs.
public sealed class InputMap{ public enum StickDirection { None, Left, Right, Up, Down } public InputMap Bind(string action, params Key[] keys) public InputMap Bind(string action, Key[] keys, PadButton[] buttons, StickDirection stick = StickDirection.None) public IEnumerable<string> Actions => _bindings.Keys; public void Sample(IInputSource source) public void SetForced(string action, bool down) public void EndUpdate() public bool Down(string action) public bool Pressed(string action) public bool Released(string action) public int Axis(string negative, string positive) public void Reset() public static InputMap CreateDefault()}Bind adds to an existing action. SetForced is how scripts and tests press actions. Default bindings are listed
in Configuration.
SaveStore and ISaveStorage
src/MicroEngine/Core/SaveStore.cs.
public sealed class SaveStore{ public static Func<string, ISaveStorage>? DefaultStorage { get; set; } public string Directory => _storage.Location; public SaveStore(string gameId, string? directory = null) public SaveStore(ISaveStorage storage) public static string DefaultDirectory(string gameId) public T? Load<T>(string slot) where T : class public bool Save<T>(string slot, T data) public bool Exists(string slot) => _storage.Exists(slot); public void Delete(string slot) => _storage.Delete(slot);}
public interface ISaveStorage{ string Location { get; } string? Read(string slot); void Write(string slot, string text); bool Exists(string slot); void Delete(string slot); void SetAside(string slot);}| Rule | Behaviour |
|---|---|
| Missing or corrupt slot | Load returns null; corrupt data is set aside (<slot>.corrupt.json for files) |
| Unreadable slot (I/O or access error) | Load returns null and that store never writes the slot |
| Refused write | Save returns false; the game keeps running |
| Where | Files under DefaultDirectory(gameId) by default (local application data on Windows and macOS, the XDG data folder on Linux); localStorage in the browser via DefaultStorage |
Implementations: FileSaveStorage (core), BrowserSaveStorage (MicroEngine.Raylib). See
Persistence.
Achievement and AchievementTracker
src/MicroEngine/Core/Achievements.cs.
public sealed record Achievement(string Id, string Name, string Description, int Goal = 1){ public Sprite? Icon { get; init; }}
public sealed class AchievementTracker{ public const string SaveSlot = "achievements"; public event Action<Achievement>? Unlocked; public AchievementTracker(IEnumerable<Achievement> catalog, SaveStore? store = null) public IReadOnlyList<Achievement> Catalog => _catalog; public int Count => _catalog.Count; public int UnlockedCount => // ... public bool HasUnsavedChanges { get; private set; } public IReadOnlyList<AchievementStatus> All => // ... public AchievementStatus Get(string id) public bool IsUnlocked(string id) => Get(id).Unlocked; public int Progress(string id) => Get(id).Progress; public bool Unlock(string id) public bool AddProgress(string id, int amount = 1) public bool SetProgress(string id, int value) public bool Flush()}Id is the saved key; keep it stable. Goal 1 is immediate, larger goals are progressive. Unlock,
AddProgress and SetProgress return true only on the call that unlocks; unknown ids throw. See
Achievements.
Running a game
| Type | Signature | Source |
|---|---|---|
DesktopLauncher |
public static int Run(Game game, string[] args) |
DesktopLauncher.cs |
BrowserLauncher |
public static GameSession CreateSession(Func<Game> createGame) |
BrowserLauncher.cs |
GameRunner |
public static RunResult Run(Game game, IPlatform platform, RunOptions? options = null) |
GameRunner.cs |
GameSession |
GameSession(Func<Game> createGame, Func<Game, IPlatform> createPlatform); Start, Tick, Stop, Result |
GameSession.cs |
RunResult |
record RunResult(long Updates, long Frames, IReadOnlyList<string> Captures, Exception? Error) |
GameRunner.cs |