scene/menu_scene_base¶
Goal¶
Build a production-style menu using BaseMenuScene with:
menu navigation (
UP,DOWN,ENTER)command-driven actions
dynamic menu labels (
label_fn)scene transition from menu -> preview -> menu
Why this tutorial exists¶
BaseMenuScene is the menu foundation used in shipped games
(deja-bounce, asteroids, space-invaders). This tutorial isolates that
pattern in a small example before pause overlays and complex gameplay scenes.
Source map¶
Settings profile:
examples/settings/scene/menu_scene_base.ymlExample builder:
examples/catalog/scene/menu_scene_base/main.pyMenu scene:
examples/catalog/scene/menu_scene_base/scenes/menu.pyCommands:
examples/catalog/scene/menu_scene_base/scenes/commands.pyPreview scene:
examples/catalog/scene/menu_scene_base/scenes/preview.pyCore menu base:
packages/mini-arcade-core/src/mini_arcade_core/ui/menu.py
What BaseMenuScene gives you¶
When your scene extends BaseMenuScene, the base class wires:
MenuInputSystem:InputFrame -> MenuIntent(UP,DOWN,ENTER/SPACE,ESC)MenuNavigationSystem: selection movement + cooldown behaviorMenuActionSystem: executes selectedMenuItem.command_factory()MenuRenderSystem: renders menu through queued UI draw op
You only implement:
menu_titlepropertymenu_style()returningMenuStylemenu_items()returninglist[MenuItem]optional
quit_command()override forESC
Example architecture¶
This tutorial uses two scenes:
menu_scene_base_menu:BaseMenuScenesubclass with three items.menu_scene_base_preview: simpleSimScenethat shows selected difficulty and returns to menu onESC.
Menu items:
START PREVIEW->StartPreviewCommand(ChangeSceneCommand)DIFFICULTY: <LEVEL>->CycleDifficultyCommandQUIT->QuitCommand
Dynamic label pattern (label_fn)¶
MenuItem supports label recomputation each frame:
MenuItem(
"difficulty",
"DIFFICULTY",
CycleDifficultyCommand,
label_fn=self._difficulty_label,
)
The callback reads runtime settings:
@staticmethod
def _difficulty_label(ctx: RuntimeContext) -> str:
level = str(ctx.settings.difficulty.level).upper()
return f"DIFFICULTY: {level}"
CycleDifficultyCommand mutates context.settings.difficulty.level, and the
next tick redraws the new label automatically.
Config requirements¶
This tutorial profile includes:
scene.initial_scene: menu_scene_base_menuscene discovery package for this example
mini_arcade_core.scenesfor built-in overlay (F1)
scene:
initial_scene: menu_scene_base_menu
scene_registry:
discover_packages:
- examples.catalog.scene.menu_scene_base
- mini_arcade_core.scenes
Run¶
Default:
mini-arcade run --example scene/menu_scene_base
Force pygame:
mini-arcade run --example scene/menu_scene_base --pass-through --backend pygame
Force native:
mini-arcade run --example scene/menu_scene_base --pass-through --backend native
What to verify¶
UP/DOWNchanges selected menu item.ENTERonSTART PREVIEWmoves to preview scene.ESCin preview returns to menu.selecting
DIFFICULTYcycles the value (EASY/NORMAL/HARD/INSANE).difficulty value persists into preview scene text.
F1toggles built-in debug overlay in both scenes.
How this maps to real games¶
Real game menu scenes follow the same base contract:
games/deja-bounce/src/deja_bounce/scenes/menu.pygames/asteroids/src/asteroids/scenes/menu.pygames/space-invaders/src/space_invaders/scenes/menu.py
Pause menus in those games also extend BaseMenuScene, but with overlay style
and different quit behavior.
Related concepts¶
Menu internals: ../../concepts/menu_scenes.md
Scene stack internals: ../../concepts/scenes_internals.md
Scene transitions internals: ../../concepts/scene_transitions.md
Configuration internals: ../../concepts/configuration.md
Next step¶
Deep-dive transition behavior: change_scene.md