Configuration Internals¶
Purpose¶
This page explains how YAML config is loaded, merged, normalized, and injected into runtime for both examples and games.
Runtime entrypoints that consume config¶
Games (reference pattern):
games/*/src/<game>/app.pySettings.for_game("<game-id>", required=True)BackendLoader.load_backend(settings.backend_defaults(...))run_game(engine_config=..., scene_config=..., gameplay_config=...)
Examples (tutorial pattern):
examples/catalog/**/main.pySettings.for_example("<example-id>", required=False)ExampleSpecbuilt from settings + CLI overrides
Settings loader behavior¶
Implementation: packages/mini-arcade/src/mini_arcade/modules/settings/__init__.py
Key points:
Scope-aware profile loading:
game:
games/<game-id>/settings/settings.yml|yamlexample:
shared:
examples/settings/settings.yml|yamloverlay:
examples/settings/<example-id>.yml|yaml
Merging:
deep merge for dict sections
later files override earlier files
Optional explicit override:
constructor
config_path=...env var
MINI_ARCADE_CONFIG_PATH
Profile caching:
Settingsinstances are cached by(config_path, scope, name)pass
force_reload=Trueif you need a fresh read
YAML structure used by engine¶
The framework reads these sections:
scene->scene_defaults()engine_config->engine_config_defaults()backend->backend_defaults()gameplay->gameplay_defaults()
Recommended schema:
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/my_font.ttf
size: 24
audio:
enable: true
sounds:
select: sfx/select.wav
gameplay:
difficulty:
default: normal
Path token resolution¶
Supported placeholders (resolved by Settings.resolve_path()):
${settings_dir}${project_root}${assets_root}${repo_root}${cwd}
backend_defaults(resolve_paths=True) resolves:
backend.fonts[*].pathbackend.audio.sounds[*](resolved relative to assets root)
How config becomes runtime objects¶
Game bootstrap pattern:
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("deja-bounce", 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,
)
Inside run_game(...) (mini_arcade_core.__init__):
engine_configdict ->EngineConfig.from_dict(...)scene_configdict ->SceneConfig.from_dict(...)SceneRegistry.discover(*scene_cfg.discover_packages)Engine(...).run(initial_scene=scene_cfg.initial_scene)
CLI interaction with YAML¶
Examples:
CLI
--pass-throughvalues are parsed byexamples/_shared/run_example.pybuilder code merges them on top of settings values
Games:
CLI runner forwards
--pass-throughargs to gamemanage.pyif your game parses args, those can override YAML at game level
the current reference games primarily rely on YAML + internal code defaults
Where to look in repository¶
settings loader:
packages/mini-arcade/src/mini_arcade/modules/settings/__init__.pybackend loader:
packages/mini-arcade/src/mini_arcade/modules/backend_loader/__init__.pygame launch wiring:
games/deja-bounce/src/deja_bounce/app.pygames/asteroids/src/asteroids/app.pygames/space-invaders/src/space_invaders/app.pyexample launch wiring:
examples/_shared/runner.pyexamples/_shared/run_example.py