config/engine_config_basics

Goal

Understand how Mini Arcade builds runtime config from:

  1. settings files

  2. CLI passthrough overrides

  3. example builder defaults

This tutorial is the reference for EngineConfig + SceneConfig wiring.

For full game scaffolding (entities, systems, draw ops), see ../create_game.md.

Why this example matters

Most real projects need:

  • backend selection per environment (pygame in dev, native for parity tests)

  • runtime tuning (fps, virtual_resolution, profiler, postfx) without code edits

  • one scene implementation that remains backend-agnostic

This example proves that workflow end-to-end.

Source map

  • Settings profile: examples/settings/config/engine_config_basics.yml

  • Example builder: examples/catalog/config/engine_config_basics/main.py

  • Shared runner: examples/_shared/runner.py

  • Runtime scene: examples/catalog/config/engine_config_basics/scenes/scene.py

Runtime flow (actual)

  1. CLI resolves example id config/engine_config_basics.

  2. Shared runner imports examples.catalog.config.engine_config_basics.main.

  3. build_example(**kwargs) loads settings with: Settings.for_example("config/engine_config_basics", required=False).

  4. Builder merges:

    • shared example defaults (examples/settings/settings.yml)

    • example profile (examples/settings/config/engine_config_basics.yml)

    • CLI passthrough overrides (--backend, --fps, etc.)

  5. Runner constructs:

    • backend instance via backend_factory()

    • EngineConfig via engine_config_factory(...)

    • SceneConfig from ExampleSpec fields

  6. Scene renders effective runtime values on screen for inspection.

Config mapping table

Input key

Source section

Final runtime target

backend.provider / --backend

settings + CLI

backend implementation (NativeBackend or PygameBackend)

engine_config.fps / --fps

settings + CLI

EngineConfig.fps

engine_config.virtual_resolution / --virtual-width --virtual-height

settings + CLI

EngineConfig.virtual_resolution

engine_config.enable_profiler / --enable-profiler

settings + CLI

EngineConfig.enable_profiler

engine_config.postfx.enabled / --postfx-enabled

settings + CLI

EngineConfig.postfx.enabled

engine_config.postfx.active / --postfx-active

settings + CLI

EngineConfig.postfx.active

scene.initial_scene

settings

SceneConfig.initial_scene

scene.scene_registry.discover_packages

settings

SceneConfig.discover_packages

backend.window.* / --window-width --window-height

settings + CLI

backend window settings

backend.renderer.background_color

settings

backend renderer settings

YAML structure for real games

This example uses example-scoped YAML, but the same model is used by games in:

  • games/<game-id>/settings/settings.yml

Recommended game profile shape:

scene:
  initial_scene: menu
  scene_registry:
    discover_packages:
      - my_game.scenes
      - mini_arcade_core.scenes

engine_config:
  fps: 60
  virtual_resolution: [960, 540]
  enable_profiler: false
  postfx:
    enabled: false
    active: []

backend:
  provider: native # or pygame
  window:
    width: 960
    height: 540
    title: My Game
    resizable: true
  renderer:
    background_color: [20, 20, 20]
  fonts:
    - name: default
      path: ${assets_root}/fonts/MyFont.ttf
      size: 24
  audio:
    enable: true
    sounds:
      select: sfx/select.wav

gameplay:
  difficulty:
    default: normal

How YAML gets into the engine/game

Reference flow from current games (deja-bounce, asteroids, space-invaders):

from mini_arcade.modules.backend_loader import BackendLoader
from mini_arcade.modules.settings import Settings
from mini_arcade_core import run_game

settings = Settings.for_game("my-game", required=True)

backend_cfg = settings.backend_defaults(resolve_paths=True)
backend = BackendLoader.load_backend(backend_cfg)

engine_cfg = settings.engine_config_defaults()
scene_cfg = settings.scene_defaults()
gameplay_cfg = settings.gameplay_defaults()

run_game(
    engine_config=engine_cfg,
    scene_config=scene_cfg,
    backend=backend,
    gameplay_config=gameplay_cfg,
)

What the framework does with each payload:

  • backend_cfg: selects and configures backend implementation from backend.provider

  • engine_cfg: converted to EngineConfig (fps, virtual resolution, postfx, profiler)

  • scene_cfg: converted to SceneConfig, then scene discovery/import runs

  • gameplay_cfg: injected into GamePlaySettings and exposed via runtime context

Deep-dive docs

Developer internals:

Precedence rules

Highest to lowest:

  1. CLI passthrough args

  2. Example-specific settings profile

  3. Shared example settings profile

  4. Hardcoded fallback values in main.py

Run

Default:

mini-arcade run --example config/engine_config_basics

Swap backend:

mini-arcade run --example config/engine_config_basics --pass-through --backend pygame
mini-arcade run --example config/engine_config_basics --pass-through --backend native

Override runtime timing/resolution:

mini-arcade run --example config/engine_config_basics --pass-through --fps 72 --virtual-width 960 --virtual-height 540

Enable profiler + postfx:

mini-arcade run --example config/engine_config_basics --pass-through --enable-profiler --postfx-enabled --postfx-active crt,vignette_noise

What to verify on-screen

The scene prints these values directly from runtime config/services:

  • backend class name

  • fps target

  • virtual resolution

  • postfx enabled + active list

  • profiler enabled flag

  • real window size + viewport mode + viewport scale

If a CLI override works, those lines change immediately on next launch.

Practical checks

  1. Set --virtual-width 960 --virtual-height 540 and confirm line changes.

  2. Run once on pygame and once on native; backend line must change, scene code must not.

  3. Enable --postfx-enabled and pass --postfx-active list; scene should display active ids.

  4. Resize window and watch viewport telemetry (mode, scale).

Common mistakes

  • Passing overrides without --pass-through: args are consumed by the top-level CLI instead of the example runner.

  • Forgetting discovery package: if discover_packages misses your module, scene registration is not found.

  • Assuming window size equals virtual resolution: they are independent (virtual is simulation/render target, window is presentation).

Controls

  • F1: toggle built-in debug overlay

  • ESC: exit

Next step