Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
SIMVX
HomeExamplesEditorDocs
SimVX documentation
SimVX documentation

Getting Started

  • Installation
  • Quickstart
  • Your First 2D Game
  • Your First 3D Game
  • From Monolithic to Composed
  • Patterns
  • Examples Gallery
    • Animated Sprite
    • Area2D
    • Bloom 2D
    • Bunnymark
    • Camera2D
    • Collision Shapes
    • Blend Modes
    • GPU Particle Fireworks
    • GPU Particles 2D
    • 2D Joints
    • Game feel
    • Layer post
    • 2D Lighting
    • Line joints and caps
    • Navigation
    • NinePatchRect
    • 2D Path Follow
    • Post shader
    • Procedural dungeon
    • Screen Effects 2D
    • Skeleton2D
    • Sprite
    • Texture resource
    • TileMap
    • Timer
    • 2D Trail
    • Triangle
    • Tween
    • Animated Model
    • Animation Blend
    • Chase Camera
    • Physics Raycast Sandbox
    • Compressed texture
    • CSG
    • Custom post-process
    • Custom Shader
    • Custom Shader Textures
    • Cutout shadows
    • Decals
    • Dynamic procedural sky
    • Extrude Path
    • First-Person Camera
    • Fog
    • GPU Particles 3D
    • IBL
    • 3D Joints
    • 3D Lighting
    • LUT Grading
    • Transform Hierarchy
    • Model Viewer
    • Motion Blur
    • MultiMesh
    • 3D Navigation
    • Noise
    • Normal mapping
    • Occlusion culling
    • FFT ocean
    • Particles
    • 3D Path Follow
    • Mouse Picking
    • Planar reflection
    • Planar water
    • Point and spot shadows
    • Post-processing
    • Irradiance probe volume
    • Rain and wetness
    • Raycast
    • ReflectionProbe3D
    • Screen-space refraction
    • Render Layers
    • Render scale
    • RenderView
    • Rotating Cube
    • Shadow caster budget
    • Shadow Quality
    • Skeletal Animation
    • Split Screen
    • SSAO
    • Screen-space global illumination
    • Screen-space reflections
    • SubViewport
    • Per-view occlusion
    • TAA
    • TAAU
    • Rendering text
    • Procedural textures
    • Volumetric Fog
    • Water surface
    • World 2D in HDR
    • Agent Playtest
    • Real LLM loop against your OpenWebUI / vLLM / llama.cpp endpoint:
    • Offline (no endpoint): runs a scripted walk-through of the session verbs.
    • LLM NPC Flavour
    • Node-Gen
    • AI state machine
    • Steering behaviours
    • Audio Playback
    • Audio Buses
    • Music Streaming
    • Spatial Audio
    • Audio Spectrum
    • Lifecycle
    • Properties
    • SaveManager
    • Scene files
    • Signals
    • Debug Draw
    • Debug Overlay
    • Python Console
    • Terminal
    • Named input actions
    • Gamepad tester
    • Input Remapping
    • Body and World Knobs
    • Character Platformer
    • Character Presence
    • Character Push
    • Scaled Colliders
    • 2D Physics Playground
    • 3D Physics Playground
    • Sleeping Bodies
    • Dialogue Box
    • Touch Gestures
    • Health bars
    • HUD Anchors
    • Internationalisation
    • Grid Inventory
    • Menu Bar
    • Minimap
    • Pause Menu
    • Settings Screen
    • Splash Screen
    • Text Input
    • AppTheme presets
    • TreeView
    • Undo/Redo demo
    • Widget Showcase
    • Bouncing Balls
    • Bouncing Balls
    • First Scene
    • First Scene
    • Gem Collector
    • Gem Collector
    • Input and Movement
    • Input and Movement
    • Monolith to Composed
    • Monolith to Composed
    • Nodes and Signals
    • Nodes and Signals
    • Pong
    • Pong
    • Testing Your Game
    • Testing Your Game
      • test_game.py
    • Balatro Feel
    • Balatro Feel (SimVX port)
      • harness.py
      • nodes/__init__.py
      • nodes/card.py
      • nodes/card_textures.py
      • nodes/hand_holder.py
    • Casual Crusade
    • Casual Crusade (SimVX port)
      • harness.py
      • nodes/__init__.py
      • nodes/card.py
      • nodes/card_data.py
      • nodes/constants.py
      • nodes/dude.py
      • nodes/game.py
      • nodes/tile.py
    • Claustrowordia
    • Claustrowordia (SimVX port)
      • harness.py
      • nodes/__init__.py
      • nodes/audio.py
      • nodes/dictionary.py
      • nodes/game.py
      • nodes/grid.py
      • nodes/hand.py
      • nodes/textures.py
      • nodes/tile.py
    • Clear Code Zelda
    • Clear Code Zelda: SimVX port
      • capture.py
      • harness.py
      • nodes/__init__.py
      • nodes/anim_sprite.py
      • nodes/enemy.py
      • nodes/level.py
      • nodes/magic.py
      • nodes/menu.py
      • nodes/particles.py
      • nodes/player.py
      • nodes/touch.py
      • nodes/ui.py
      • nodes/upgrade.py
      • nodes/weapon.py
      • settings.py
      • support.py
    • Clumsy Bird
    • Clumsy Bird (SimVX port)
      • config.py
      • nodes/__init__.py
      • nodes/audio.py
      • nodes/bird.py
      • nodes/ground.py
      • nodes/hud.py
      • nodes/menu.py
      • nodes/pipe.py
      • nodes/play_scene.py
    • Dodge the Creeps
      • capture.py
      • nodes/__init__.py
      • nodes/audio.py
      • nodes/hud.py
      • nodes/mob.py
      • nodes/player.py
      • nodes/sprite_sheets.py
    • GDQuest Open RPG
    • GDQuest Open RPG: SimVX port
      • nodes/__init__.py
      • nodes/actions.py
      • nodes/audio_fx.py
      • nodes/battle.py
      • nodes/battler.py
      • nodes/dialogue.py
      • nodes/floating_label.py
      • nodes/gameboard.py
      • nodes/harness.py
      • nodes/hud.py
      • nodes/layout.py
      • nodes/npc.py
      • nodes/overworld.py
      • nodes/player.py
      • nodes/save_io.py
      • nodes/screen_transition.py
      • nodes/settings.py
      • nodes/sprites.py
      • nodes/stats.py
    • HeartBeast Action RPG
    • HeartBeast Action RPG: SimVX Port
      • nodes/__init__.py
      • nodes/effects.py
      • nodes/enemy.py
      • nodes/grass.py
      • nodes/hud.py
      • nodes/player.py
      • nodes/stats.py
      • nodes/ui.py
      • nodes/world.py
      • settings.py
    • HexGL
    • HexGL: SimVX Port
      • harness.py
      • nodes/__init__.py
      • nodes/ai_ship.py
      • nodes/camera.py
      • nodes/hud.py
      • nodes/menu.py
      • nodes/race.py
      • nodes/ship.py
      • nodes/track.py
    • Hextris
    • Hextris (SimVX port)
      • capture.py
      • nodes/__init__.py
      • nodes/game.py
      • nodes/hex_math.py
    • Mr. Rescue
    • Mr. Rescue: SimVX port
      • harness.py
      • nodes/__init__.py
      • nodes/audio.py
      • nodes/building.py
      • nodes/civilian.py
      • nodes/colours.py
      • nodes/enemy.py
      • nodes/fire.py
      • nodes/game.py
      • nodes/hud.py
      • nodes/menu.py
      • nodes/particles.py
      • nodes/player.py
      • nodes/textures.py
      • nodes/tile_grid.py
    • PirateMaker
    • PirateMaker: SimVX port
      • harness.py
      • nodes/__init__.py
      • nodes/editor.py
      • nodes/enemies.py
      • nodes/folder_sprite.py
      • nodes/hud.py
      • nodes/level.py
      • nodes/player.py
      • nodes/save_load.py
      • settings.py
      • sfx.py
      • support.py
    • Procedural Planets
    • Procedural Planets: SimVX Port
      • harness.py
      • nodes/__init__.py
      • nodes/colour.py
      • nodes/controls.py
      • nodes/planet.py
      • nodes/shape.py
      • nodes/terrain_face.py
      • nodes/title.py
    • Q1K3
    • Q1K3: SimVX port
      • harness.py
      • nodes/__init__.py
      • nodes/audio.py
      • nodes/door.py
      • nodes/enemy.py
      • nodes/hud.py
      • nodes/light.py
      • nodes/maps.py
      • nodes/mathutil.py
      • nodes/menu.py
      • nodes/meshes.py
      • nodes/particle.py
      • nodes/physics.py
      • nodes/pickup.py
      • nodes/player.py
      • nodes/projectile.py
      • nodes/prop.py
      • nodes/root.py
      • nodes/textures.py
      • nodes/touch.py
      • nodes/weapon.py
      • nodes/world.py
    • Asteroids (raylib classic)
      • __init__.py
      • nodes/__init__.py
      • nodes/asteroids_game.py
    • Snake (raylib classic)
    • Snake (raylib classic): SimVX port
      • __init__.py
      • nodes/__init__.py
      • nodes/snake_game.py
    • Tetris (raylib classic)
    • Tetris (raylib classic): SimVX port
      • __init__.py
      • nodes/__init__.py
      • nodes/tetris_game.py
    • SNKRX
    • SNKRX (SimVX port)
      • harness.py
      • nodes/__init__.py
      • nodes/arena.py
      • nodes/buy_screen.py
      • nodes/colours.py
      • nodes/enemies.py
      • nodes/hud.py
      • nodes/menu.py
      • nodes/particles.py
      • nodes/projectile.py
      • nodes/units.py
    • Klondike Solitaire
    • Klondike Solitaire: SimVX port
      • harness.py
      • nodes/card_node.py
      • nodes/card_textures.py
      • nodes/game_state.py
      • nodes/hud.py
      • nodes/menu.py
      • nodes/save_io.py
      • nodes/table.py
    • Squash the Creeps
      • nodes/__init__.py
      • nodes/arena.py
      • nodes/mob.py
      • nodes/player.py
    • Tanks of Freedom
    • Tanks of Freedom: SimVX port
      • harness.py
      • nodes/__init__.py
      • nodes/ai.py
      • nodes/building.py
      • nodes/combat.py
      • nodes/cursor.py
      • nodes/data.py
      • nodes/effects.py
      • nodes/hud.py
      • nodes/pathfinder.py
      • nodes/textures.py
      • nodes/tile_map.py
      • nodes/turn.py
      • nodes/unit.py
      • nodes/world.py
    • Tiny Yurts
    • Tiny Yurts (SimVX port)
      • harness.py
      • nodes/__init__.py
      • nodes/grid.py
      • nodes/iso.py
      • nodes/settler.py
      • nodes/world.py
    • Tower Defence
    • Tower Defence (SimVX port)
      • harness.py
      • nodes/__init__.py
      • nodes/audio.py
      • nodes/enemy.py
      • nodes/level_data.py
      • nodes/td_data.py
      • nodes/td_world.py
      • nodes/turret.py
    • Pixel Runner
    • Pixel Runner: SimVX Port
      • __init__.py
      • nodes/__init__.py
      • nodes/assets.py
      • nodes/audio.py
      • nodes/obstacle.py
      • nodes/player.py
      • nodes/runner.py
    • You’re the OS
    • You’re the OS: SimVX port
      • harness.py
      • nodes/__init__.py
      • nodes/stage.py
      • nodes/state.py
      • nodes/theme.py
    • Afterglow
    • Afterglow: original precision platformer (SimVX flagship demo)
      • afterglow/__init__.py
      • afterglow/assets/__init__.py
      • afterglow/assets/audio.py
      • afterglow/assets/sprite_gen.py
      • afterglow/assets/textures.py
      • afterglow/game.py
      • afterglow/progress.py
      • afterglow/sim/__init__.py
      • afterglow/sim/entities.py
      • afterglow/sim/room.py
      • afterglow/sim/rooms_data.py
      • afterglow/sim/tiles.py
      • afterglow/ui/__init__.py
      • afterglow/ui/controls.py
      • afterglow/ui/hud.py
      • afterglow/ui/menus.py
      • afterglow/view/__init__.py
      • afterglow/view/camera.py
      • afterglow/view/diorama.py
      • afterglow/view/effects.py
      • afterglow/view/environment.py
      • afterglow/view/lighting.py
      • afterglow/view/sprites.py
      • afterglow/view/title_backdrop.py
    • Asteroids 2D
    • Bloom
    • Bloom
      • ai.py
      • app.py
      • board.py
      • game.py
      • hex_grid.py
      • menu.py
      • render.py
      • web.py
    • Deep Sea Aquarium
      • __init__.py
      • boids.py
      • creatures.py
      • environment.py
      • meshgen.py
      • music.py
    • Dungeon Explorer
      • collision_layers.py
      • data/__init__.py
      • data/dialogs.py
      • events.py
      • nodes/__init__.py
      • nodes/_back_button.py
      • nodes/abilities.py
      • nodes/achievements.py
      • nodes/audio_manager.py
      • nodes/boss_enemy.py
      • nodes/boss_health_bar.py
      • nodes/camera_controller.py
      • nodes/combat.py
      • nodes/confirm_dialog.py
      • nodes/credits_screen.py
      • nodes/death_screen.py
      • nodes/dialog_ui.py
      • nodes/enchanter.py
      • nodes/enemy_base.py
      • nodes/enemy_types.py
      • nodes/game_manager.py
      • nodes/hotbar_assign_ui.py
      • nodes/inn.py
      • nodes/inventory_ui.py
      • nodes/item_tooltip.py
      • nodes/level_up_ui.py
      • nodes/loading_screen.py
      • nodes/loot_drop.py
      • nodes/loot_pickup.py
      • nodes/particles2d.py
      • nodes/pause_menu.py
      • nodes/player.py
      • nodes/player_hud.py
      • nodes/player_pause_bridge.py
      • nodes/projectile.py
      • nodes/quest_board.py
      • nodes/quest_log_ui.py
      • nodes/settings_ui.py
      • nodes/shop.py
      • nodes/skill_tree_ui.py
      • nodes/stats_tracker.py
      • nodes/title_screen.py
      • nodes/town.py
      • nodes/town_npc.py
      • nodes/transition_overlay.py
      • nodes/victory_screen.py
      • nodes/village.py
      • nodes/waypoint_ui.py
      • nodes/well.py
      • scripts/__init__.py
      • scripts/blacksmith.py
      • scripts/click_controller.py
      • scripts/dungeon_generator.py
      • scripts/dungeon_theme.py
      • scripts/enemy_spawner.py
      • scripts/fog_of_war.py
      • scripts/inventory.py
      • scripts/item.py
      • scripts/item_generator.py
      • scripts/mini_boss.py
      • scripts/player_skills.py
      • scripts/player_stats.py
      • scripts/quests.py
      • scripts/room_templates.py
      • scripts/save_state.py
      • scripts/tutorial.py
      • scripts/weapons.py
    • Pad Grid
    • Physics Sandbox
    • Planet Explorer
    • SHRIKE
    • SHRIKE
      • shrike/__init__.py
      • shrike/artkit.py
      • shrike/audio.py
      • shrike/balance.py
      • shrike/bounty.py
      • shrike/chart.py
      • shrike/combat.py
      • shrike/enemies/__init__.py
      • shrike/enemies/advanced.py
      • shrike/enemies/basic.py
      • shrike/enemies/steering.py
      • shrike/events.py
      • shrike/flow.py
      • shrike/hud.py
      • shrike/hunter.py
      • shrike/juice.py
      • shrike/meta.py
      • shrike/modules.py
      • shrike/onboarding.py
      • shrike/power.py
      • shrike/roost.py
      • shrike/runtime.py
      • shrike/save.py
      • shrike/sector.py
      • shrike/ship.py
      • shrike/signature.py
      • shrike/trading.py
      • shrike/vfx.py
      • shrike/waves.py
      • shrike/weapons.py
    • Space Invaders 2D
    • Space Invaders 3D
    • Squad Commander
    • Tic Tac Toe
      • demo.py
      • game.py
      • menu.py
      • web.py
    • Tidewater
  • Architecture
  • Building a Simple Game with the SimVX Editor
  • Shipping a Game

Core Engine

  • Core Engine
  • Node System
  • Input System
  • Windowing Backends
  • Animation
  • SimVX Audio System
  • AssetServer
  • UI System
  • Fonts and Languages
  • Math Types
  • Physics
  • Scenes
  • Particles
  • TileMap
  • 2D Lighting
  • Constructive Solid Geometry
  • Navigation
  • 3D Navigation
  • Skeleton
  • Physics Backends
  • WorldEnvironment
  • Testing
  • Performance regression suite
  • Package Resources
  • Save System
  • Event Bus

Graphics Backend

  • Graphics Backend
  • App Class
  • Cameras (3D)
  • Draw2D: immediate-mode 2D drawing
  • Materials
  • Compressed Textures (BC / ASTC / ETC2)
  • Blender to SimVX (glTF)
  • Custom Shaders
  • Visual Testing

Web

  • Web Export
  • Web export: resource channel

IDE

  • IDE

API Reference

  • API Reference
    • simvx.core
      • simvx.core.assets
        • simvx.core.assets.sources
          • simvx.core.assets.sources.mem
          • simvx.core.assets.sources.file
          • simvx.core.assets.sources.http
          • simvx.core.assets.sources.pkg
        • simvx.core.assets.loaders
          • simvx.core.assets.loaders.bytes_loader
          • simvx.core.assets.loaders.json_loader
          • simvx.core.assets.loaders.base
        • simvx.core.assets.server
        • simvx.core.assets.handle
        • simvx.core.assets.cache
        • simvx.core.assets.source
      • simvx.core.navigation
        • simvx.core.navigation.path_follower_2d
      • simvx.core.port_helpers
        • simvx.core.port_helpers.procedural_textures
      • simvx.core.ui
        • simvx.core.ui.ansi_parser
        • simvx.core.ui.splash
        • simvx.core.ui.rich_text
        • simvx.core.ui.advanced
        • simvx.core.ui.widgets
        • simvx.core.ui.markers
        • simvx.core.ui.text_paragraph
        • simvx.core.ui.navigation
        • simvx.core.ui.testing
        • simvx.core.ui.overlay
        • simvx.core.ui.containers
        • simvx.core.ui.clipboard
        • simvx.core.ui.file_browser
        • simvx.core.ui.terminal
        • simvx.core.ui.theme
        • simvx.core.ui.syntax_highlighter
        • simvx.core.ui.tooltip
        • simvx.core.ui.core
        • simvx.core.ui.dock
        • simvx.core.ui.find_replace
        • simvx.core.ui.tabs
        • simvx.core.ui.tree
        • simvx.core.ui.split
        • simvx.core.ui.graph_edit
        • simvx.core.ui.virtual_controls
        • simvx.core.ui.virtual_scroll
        • simvx.core.ui.completion_types
        • simvx.core.ui.file_dialog
        • simvx.core.ui.gamepad_overlay
        • simvx.core.ui.multiline
        • simvx.core.ui.focus
        • simvx.core.ui.scroll
        • simvx.core.ui.toolbar
        • simvx.core.ui.menu
        • simvx.core.ui.grid_slots
        • simvx.core.ui.code_editor_panel
        • simvx.core.ui.marks
        • simvx.core.ui.bottom_strip
        • simvx.core.ui.autocomplete
        • simvx.core.ui.folding
        • simvx.core.ui.colour_picker
        • simvx.core.ui.enums
        • simvx.core.ui.code_edit
        • simvx.core.ui.types
        • simvx.core.ui.ui_input
      • simvx.core.text
        • simvx.core.text.measure
        • simvx.core.text.msdf
        • simvx.core.text.font
        • simvx.core.text.bundled
      • simvx.core.scene_io
        • simvx.core.scene_io.symbols
        • simvx.core.scene_io.scene_file
        • simvx.core.scene_io.emitter
        • simvx.core.scene_io.layout
        • simvx.core.scene_io.source_tree
        • simvx.core.scene_io.edits
        • simvx.core.scene_io.scene_module
        • simvx.core.scene_io.loader
        • simvx.core.scene_io.detection
        • simvx.core.scene_io.syntax
      • simvx.core.input
        • simvx.core.input.gamepad
        • simvx.core.input.web_events
        • simvx.core.input.state
        • simvx.core.input.router
        • simvx.core.input.map
        • simvx.core.input.events
        • simvx.core.input.enums
      • simvx.core.testing
        • simvx.core.testing.scene_runner
        • simvx.core.testing.recorder
        • simvx.core.testing.optional
        • simvx.core.testing.selftest
        • simvx.core.testing.diagnostics
        • simvx.core.testing.benchmark
        • simvx.core.testing.input_sim
      • simvx.core.navigation3d
        • simvx.core.navigation3d.server
        • simvx.core.navigation3d.nodes
        • simvx.core.navigation3d.mesh
      • simvx.core.nodes_2d
        • simvx.core.nodes_2d.marker
        • simvx.core.nodes_2d.camera
        • simvx.core.nodes_2d.remote_transform
        • simvx.core.nodes_2d.trail
        • simvx.core.nodes_2d.ninepatch
        • simvx.core.nodes_2d.canvas
        • simvx.core.nodes_2d.shapes
        • simvx.core.nodes_2d.node2d
        • simvx.core.nodes_2d.spring_follow
        • simvx.core.nodes_2d.ysort
        • simvx.core.nodes_2d.text
        • simvx.core.nodes_2d.background_bands
        • simvx.core.nodes_2d.path
      • simvx.core.math
        • simvx.core.math.aabb
        • simvx.core.math.rect2
        • simvx.core.math.matrices
        • simvx.core.math.transforms
        • simvx.core.math.raycast
        • simvx.core.math.curves
        • simvx.core.math.types
      • simvx.core.graphics
        • simvx.core.graphics.material
        • simvx.core.graphics.texture
        • simvx.core.graphics.shader_bindings
        • simvx.core.graphics.texture_slot
        • simvx.core.graphics.mesh
        • simvx.core.graphics.shader
      • simvx.core.ai
        • simvx.core.ai.sensor
        • simvx.core.ai.async_slot
        • simvx.core.ai.blackboard
        • simvx.core.ai.agent_node
        • simvx.core.ai.brain
      • simvx.core.debug
        • simvx.core.debug.ui_inspector
        • simvx.core.debug.overlay
        • simvx.core.debug.profiler
      • simvx.core.lsp
        • simvx.core.lsp.protocol
        • simvx.core.lsp.server
        • simvx.core.lsp.client
      • simvx.core.animation
        • simvx.core.animation.tween
        • simvx.core.animation.skeletal
        • simvx.core.animation.state_machine
        • simvx.core.animation.sprite
        • simvx.core.animation.player
        • simvx.core.animation.track
        • simvx.core.animation.blend_space
      • simvx.core.nodes_3d
        • simvx.core.nodes_3d.chase_camera
        • simvx.core.nodes_3d.marker
        • simvx.core.nodes_3d.camera
        • simvx.core.nodes_3d.lights
        • simvx.core.nodes_3d.node3d
        • simvx.core.nodes_3d.remote_transform
        • simvx.core.nodes_3d.multimesh
        • simvx.core.nodes_3d.spring_arm
        • simvx.core.nodes_3d.sprite
        • simvx.core.nodes_3d.text
        • simvx.core.nodes_3d.mesh
        • simvx.core.nodes_3d.path
      • simvx.core.physics
        • simvx.core.physics.builtin
          • simvx.core.physics.builtin.world2d
          • simvx.core.physics.builtin.world
        • simvx.core.physics.pymunk_backend
        • simvx.core.physics.material
        • simvx.core.physics.shapes2d
        • simvx.core.physics.nodes
        • simvx.core.physics.capability
        • simvx.core.physics.shapes
        • simvx.core.physics.world2d
        • simvx.core.physics.slide
        • simvx.core.physics.root
        • simvx.core.physics.nodes2d
        • simvx.core.physics.backends
        • simvx.core.physics.query2d
        • simvx.core.physics.world
        • simvx.core.physics.query
      • simvx.core.audio_backend
      • simvx.core.audio_synth
      • simvx.core.mesh_builder
      • simvx.core.water
      • simvx.core.particle_trail
      • simvx.core.mesh_lod
      • simvx.core.scene_target_graph
      • simvx.core.git_status
      • simvx.core.gpu_particles
      • simvx.core.planar_reflection
      • simvx.core.scene_tree
      • simvx.core.event_bus
      • simvx.core.branding
      • simvx.core.clipboard
      • simvx.core.shortcuts
      • simvx.core.decal
      • simvx.core.i18n
      • simvx.core.atlas
      • simvx.core.hot_reload
      • simvx.core.ocean
      • simvx.core.colour
      • simvx.core.save_manager
      • simvx.core.run
      • simvx.core.descriptors
      • simvx.core.resource
      • simvx.core.env_sync_spec
      • simvx.core.project
      • simvx.core.file_state
      • simvx.core.audio_bus
      • simvx.core.clustered_lighting
      • simvx.core.easing
      • simvx.core.config
      • simvx.core.selection
      • simvx.core.rain
      • simvx.core.irradiance_volume
      • simvx.core.node
      • simvx.core.gesture
      • simvx.core.document
      • simvx.core.properties
      • simvx.core.undo
      • simvx.core.skeleton2d
      • simvx.core.gizmo
      • simvx.core.shell_node
      • simvx.core.decorators
      • simvx.core.light2d
      • simvx.core.mesh_instance_2d
      • simvx.core.audio_errors
      • simvx.core.procedural_sky
      • simvx.core.cli
      • simvx.core.audio_effect
      • simvx.core.signals
      • simvx.core.noise
      • simvx.core.skeleton
      • simvx.core.audio_listener
      • simvx.core.audio_protocol
      • simvx.core.audio
      • simvx.core.scene_step
      • simvx.core.py_console
      • simvx.core.reflection_probe
      • simvx.core.export
      • simvx.core.tilemap
      • simvx.core.asset_resolver
      • simvx.core.io
      • simvx.core.viewport
      • simvx.core.events
      • simvx.core.fog_volume
      • simvx.core.graphics_quality
      • simvx.core.render_view
      • simvx.core.csg
      • simvx.core.scripted_demo
      • simvx.core.particles
      • simvx.core.coroutines
      • simvx.core.colour_grading
      • simvx.core.world_environment
    • simvx.graphics
      • simvx.graphics.materials
        • simvx.graphics.materials.material
        • simvx.graphics.materials.texture
        • simvx.graphics.materials.custom_shader
        • simvx.graphics.materials.shader_compiler
      • simvx.graphics.assets
        • simvx.graphics.assets.block_decode
        • simvx.graphics.assets.image_loader
        • simvx.graphics.assets.ktx2_loader
        • simvx.graphics.assets.mesh_loader
        • simvx.graphics.assets.cubemap_loader
        • simvx.graphics.assets.scene_import
        • simvx.graphics.assets.dds_loader
      • simvx.graphics.gpu
        • simvx.graphics.gpu.memory
        • simvx.graphics.gpu.multi_device
        • simvx.graphics.gpu.instance
        • simvx.graphics.gpu.queue
        • simvx.graphics.gpu.context
        • simvx.graphics.gpu.pipeline_cache
        • simvx.graphics.gpu.sync
        • simvx.graphics.gpu.descriptors
        • simvx.graphics.gpu.retire
        • simvx.graphics.gpu.secondary_engine
        • simvx.graphics.gpu.timestamp_pool
        • simvx.graphics.gpu.pipeline_compute
        • simvx.graphics.gpu.device
        • simvx.graphics.gpu.swapchain
        • simvx.graphics.gpu.commands
        • simvx.graphics.gpu.capabilities
        • simvx.graphics.gpu.pipeline
      • simvx.graphics.ui
      • simvx.graphics.scene
        • simvx.graphics.scene.camera
        • simvx.graphics.scene.frustum
        • simvx.graphics.scene.draw_batch
      • simvx.graphics.shaders
      • simvx.graphics.picking
        • simvx.graphics.picking.pick_pass
        • simvx.graphics.picking.raycast
      • simvx.graphics.renderer
        • simvx.graphics.renderer.sub_viewport
        • simvx.graphics.renderer.light_cull_pass
        • simvx.graphics.renderer.decal_pack
        • simvx.graphics.renderer.layer_post
        • simvx.graphics.renderer.draw2d_pass
        • simvx.graphics.renderer.bloom_pass
        • simvx.graphics.renderer.pass_helpers
        • simvx.graphics.renderer.viewport_manager
        • simvx.graphics.renderer.bindless_draw2d_pass
        • simvx.graphics.renderer.gizmo_pass
        • simvx.graphics.renderer.ssgi_pass
        • simvx.graphics.renderer.render_graph
        • simvx.graphics.renderer.pass_orchestrator
        • simvx.graphics.renderer.shadow_data
        • simvx.graphics.renderer.grid_pass
        • simvx.graphics.renderer.reflection_probe_pack
        • simvx.graphics.renderer.shadow_cache
        • simvx.graphics.renderer.hiz_pass
        • simvx.graphics.renderer.render_target
        • simvx.graphics.renderer.scene_renderer
        • simvx.graphics.renderer.shadow_renderer
        • simvx.graphics.renderer.ocean_pack
        • simvx.graphics.renderer.irradiance_volume_pass
        • simvx.graphics.renderer.render_context
        • simvx.graphics.renderer.resource_registry
        • simvx.graphics.renderer.depth_prepass
        • simvx.graphics.renderer.ssao_pass
        • simvx.graphics.renderer.particle_compute
        • simvx.graphics.renderer.environment_sync
        • simvx.graphics.renderer.game_viewport
        • simvx.graphics.renderer.water_pack
        • simvx.graphics.renderer.occlusion_cull
        • simvx.graphics.renderer.skybox_pass
        • simvx.graphics.renderer.ocean_compute
        • simvx.graphics.renderer.ibl_pass
        • simvx.graphics.renderer.velocity_pass
        • simvx.graphics.renderer.buffer_manager
        • simvx.graphics.renderer.ocean_fft
        • simvx.graphics.renderer.point_shadow_pass
        • simvx.graphics.renderer.forward
        • simvx.graphics.renderer.reflection_probe_pass
        • simvx.graphics.renderer.render_thread
        • simvx.graphics.renderer.outline_pass
        • simvx.graphics.renderer.tilemap_pass
        • simvx.graphics.renderer.particle_pass
        • simvx.graphics.renderer.text_pass
        • simvx.graphics.renderer.async_compute
        • simvx.graphics.renderer.pipeline_manager
        • simvx.graphics.renderer.gpu_batch
        • simvx.graphics.renderer.shadow_pass
        • simvx.graphics.renderer.light2d_pass
        • simvx.graphics.renderer.secondary_sru
        • simvx.graphics.renderer.vertex_layouts
        • simvx.graphics.renderer.transparency
        • simvx.graphics.renderer.view_occlusion
        • simvx.graphics.renderer.post_process
        • simvx.graphics.renderer.ssr_pass
        • simvx.graphics.renderer.mesh_registry
        • simvx.graphics.renderer.caster_slots
        • simvx.graphics.renderer.render_packet
        • simvx.graphics.renderer.scene_copy
        • simvx.graphics.renderer.billboard2d_data
        • simvx.graphics.renderer.render_pass
        • simvx.graphics.renderer.fog_volume_pack
        • simvx.graphics.renderer.billboard2d_pass
        • simvx.graphics.renderer.passes
        • simvx.graphics.renderer.overlay_renderer
        • simvx.graphics.renderer.water_pass
        • simvx.graphics.renderer.render_view
        • simvx.graphics.renderer.volumetric_fog_pass
        • simvx.graphics.renderer.custom_post_process
        • simvx.graphics.renderer.ocean_pass
        • simvx.graphics.renderer.colour_grading
        • simvx.graphics.renderer.shadow_math
        • simvx.graphics.renderer.taa_pass
        • simvx.graphics.renderer.tile_types
      • simvx.graphics.render2d
        • simvx.graphics.render2d.clip_scope
        • simvx.graphics.render2d.publish
        • simvx.graphics.render2d.submit
        • simvx.graphics.render2d.cache
        • simvx.graphics.render2d.item_list
        • simvx.graphics.render2d.item_builder
      • simvx.graphics.streaming
        • simvx.graphics.streaming.server
      • simvx.graphics.platform
      • simvx.graphics.playtest_patterns
      • simvx.graphics.text_renderer
      • simvx.graphics.draw2d_texture
      • simvx.graphics.draw2d_text
      • simvx.graphics.engine
      • simvx.graphics.testing
      • simvx.graphics.scene_adapter
      • simvx.graphics.engine_surface
      • simvx.graphics.blend
      • simvx.graphics.gpu_gate
      • simvx.graphics.draw2d_vertex
      • simvx.graphics.playtest
      • simvx.graphics.text_utils
      • simvx.graphics.cook
      • simvx.graphics.frame_loop
      • simvx.graphics.material_slots
      • simvx.graphics.pytest_plugin
      • simvx.graphics.draw2d_transform
      • simvx.graphics.cli
      • simvx.graphics.input_adapter
      • simvx.graphics.app
      • simvx.graphics.draw2d
      • simvx.graphics.draw2d_ops
      • simvx.graphics.debug_draw
      • simvx.graphics.frame_globals
      • simvx.graphics.types
    • simvx.editor
      • simvx.editor.panels
        • simvx.editor.panels.inspector_sections
        • simvx.editor.panels.scene_tree
          • simvx.editor.panels.scene_tree.type_registry
          • simvx.editor.panels.scene_tree.dialogs
          • simvx.editor.panels.scene_tree.panel
        • simvx.editor.panels.repl_panel
        • simvx.editor.panels.asset_browser
        • simvx.editor.panels.inspector_widgets
        • simvx.editor.panels.animation
        • simvx.editor.panels.profiler_panel
        • simvx.editor.panels.scene3d_view
        • simvx.editor.panels.file_browser
        • simvx.editor.panels.play_controls
        • simvx.editor.panels.code_tab
        • simvx.editor.panels.animation_editor
        • simvx.editor.panels.properties
        • simvx.editor.panels.status_bar
        • simvx.editor.panels.section_widgets
        • simvx.editor.panels.anchor_preset_widget
        • simvx.editor.panels.console
        • simvx.editor.panels.scene2d_view
      • simvx.editor.testing
        • simvx.editor.testing.demo_steps
          • simvx.editor.testing.demo_steps.handlers
            • simvx.editor.testing.demo_steps.handlers.inspector
            • simvx.editor.testing.demo_steps.handlers.nodes
            • simvx.editor.testing.demo_steps.handlers.menu
            • simvx.editor.testing.demo_steps.handlers.viewport
          • simvx.editor.testing.demo_steps.steps
      • simvx.editor.command_palette
      • simvx.editor.refactor_inline
      • simvx.editor.autosave
      • simvx.editor.web_export
      • simvx.editor.dialog_message
      • simvx.editor.refactor_extract
      • simvx.editor.unsaved_class_warning_dialog
      • simvx.editor.export_dialog
      • simvx.editor.about_dialog
      • simvx.editor.main
      • simvx.editor.gizmo_manipulator
      • simvx.editor.make_custom_class_dialog
      • simvx.editor.error_nav
      • simvx.editor.theme
      • simvx.editor.play_mode
      • simvx.editor.error_recovery
      • simvx.editor.project_settings_dialog
      • simvx.editor.project
      • simvx.editor.node_ops
      • simvx.editor.workspace_tabs
      • simvx.editor.game_render_hook
      • simvx.editor.project_classes
      • simvx.editor.config
      • simvx.editor.scene_file_ops
      • simvx.editor.state
      • simvx.editor.rename_class_dialog
      • simvx.editor.layout_presets
      • simvx.editor.default_scenes
      • simvx.editor.convert_to_class
      • simvx.editor.welcome
      • simvx.editor.export_controller
      • simvx.editor.input_map_dialog
      • simvx.editor.modal_dialog
      • simvx.editor.project_registry
      • simvx.editor.preferences_dialog
      • simvx.editor.plugin
      • simvx.editor.cli
      • simvx.editor.menus
      • simvx.editor.root
      • simvx.editor.keyboard_nav
      • simvx.editor.scene_diff
      • simvx.editor.app
      • simvx.editor.live_file_ops
      • simvx.editor.save_dialog
      • simvx.editor.extract
      • simvx.editor.save_report_dialog
      • simvx.editor.hints
      • simvx.editor.templates
      • simvx.editor.commands
      • simvx.editor.duplicate_node_dialog
    • simvx.ide
      • simvx.ide.lint
        • simvx.ide.lint.runner
      • simvx.ide.panels
        • simvx.ide.panels.terminal_panel
        • simvx.ide.panels.output_panel
        • simvx.ide.panels.debug_panel
        • simvx.ide.panels.search_panel
        • simvx.ide.panels.file_browser
        • simvx.ide.panels.symbol_outline
        • simvx.ide.panels.problems_panel
        • simvx.ide.panels.settings_panel
      • simvx.ide.dap
        • simvx.ide.dap.client
      • simvx.ide.widgets
        • simvx.ide.widgets.command_palette
        • simvx.ide.widgets.minimap
        • simvx.ide.widgets.confirm_dialog
        • simvx.ide.widgets.status_bar
        • simvx.ide.widgets.goto_line
        • simvx.ide.widgets.autocomplete
      • simvx.ide.lsp
        • simvx.ide.lsp.client
      • simvx.ide.language_server
      • simvx.ide.editor_integration
      • simvx.ide.edit_actions
      • simvx.ide.playtest
      • simvx.ide.config
      • simvx.ide.state
      • simvx.ide.embedded
      • simvx.ide.app
      • simvx.ide.file_tabs
      • simvx.ide.keybindings
      • simvx.ide.debug_session

Project

  • SimVX Documentation License
Back to top
View this page

menu.py¶

Part of Bloom.

  1"""Bloom menus: main menu (mode + difficulty) and the how-to-play card.
  2
  3Both are anchored, resize-aware UI built from SimVX widgets. The how-to card
  4runs a looping live demo of the one rule by reusing the real board, resolver
  5and :class:`HexBoardView`, so the tutorial can never drift from the game.
  6"""
  7
  8import math
  9
 10import render
 11from ai import DIFFICULTY_BLURB
 12from board import CENTRE, Board, Owner, resolve_placement
 13from hex_grid import Hex, hex_corners, hex_in_range
 14from render import HexBoardView
 15
 16from simvx.core import (
 17    AnchorPreset,
 18    Button,
 19    HBoxContainer,
 20    Label,
 21    Node2D,
 22    Signal,
 23    SizingMode,
 24    VBoxContainer,
 25    Vec2,
 26    tween,
 27    wait,
 28)
 29from simvx.core.animation.tween import ease_out_back
 30
 31TITLE = (1.0, 0.72, 0.42, 1.0)
 32TEXT = (0.93, 0.94, 0.97, 1.0)
 33TEXT_DIM = (0.62, 0.65, 0.74, 1.0)
 34BTN_BG = (0.16, 0.18, 0.24, 1.0)
 35BTN_HOVER = (0.22, 0.26, 0.34, 1.0)
 36BTN_DOWN = (0.12, 0.14, 0.19, 1.0)
 37SEL_BG = (0.30, 0.34, 0.46, 1.0)
 38PLAY_BG = (0.62, 0.34, 0.16, 1.0)
 39PLAY_HOVER = (0.74, 0.42, 0.20, 1.0)
 40
 41
 42def make_button(
 43    text, width=0.0, height=0.0, *, font=18.0, bg=BTN_BG, hover=BTN_HOVER, down=BTN_DOWN, fg=TEXT
 44) -> Button:
 45    """Themed :class:`Button` factory shared by every Bloom screen.
 46
 47    ``width`` / ``height`` may be left at 0 when a container sizes the button.
 48    """
 49    b = Button(text)
 50    b.size = Vec2(width, height)
 51    b.font_size = font
 52    b.bg_colour = bg
 53    b.hover_colour = hover
 54    b.pressed_colour = down
 55    b.text_colour = fg
 56    b.border_colour = (0.30, 0.33, 0.42, 1.0)
 57    return b
 58
 59
 60class _Segmented:
 61    """A row of mutually-exclusive option buttons."""
 62
 63    def __init__(self, options: list[tuple[str, str]], default: str, on_change):
 64        self.options = options
 65        self.value = default
 66        self._on_change = on_change
 67        self.buttons: dict[str, Button] = {}
 68        self.row = HBoxContainer()
 69        self.row.separation = 6
 70        self.row.sizing = SizingMode.FILL  # the row shares its width evenly, at any size
 71
 72    def build(self, total_w: float, h: float) -> HBoxContainer:
 73        for key, label in self.options:
 74            b = make_button(label, height=h, font=15.0)
 75            b.pressed.connect(lambda k=key: self.select(k))
 76            self.buttons[key] = b
 77            self.row.add_child(b)
 78        self.row.size = Vec2(total_w, h)
 79        self._restyle()
 80        return self.row
 81
 82    def select(self, key: str):
 83        if key == self.value:
 84            return
 85        self.value = key
 86        self._restyle()
 87        self._on_change(key)
 88
 89    def set_enabled(self, enabled: bool):
 90        for b in self.buttons.values():
 91            b.bg_colour = BTN_BG if enabled else (0.12, 0.12, 0.15, 1.0)
 92            b.text_colour = TEXT if enabled else TEXT_DIM
 93        if enabled:
 94            self._restyle()
 95
 96    def _restyle(self):
 97        for key, b in self.buttons.items():
 98            sel = key == self.value
 99            b.bg_colour = SEL_BG if sel else BTN_BG
100            b.text_colour = (1.0, 1.0, 1.0, 1.0) if sel else TEXT
101
102
103class MainMenu(Node2D):
104    """Title, mode/difficulty selectors and Play. Emits configuration on start."""
105
106    start_requested = Signal(object)  # dict: {"mode", "difficulty"}
107    help_requested = Signal()
108    quit_requested = Signal()
109
110    def __init__(self, show_quit: bool = True, **kw):
111        super().__init__(name="MainMenu", **kw)
112        self._show_quit = show_quit
113        self.mode = "ai"
114        self.difficulty = "sharp"
115        self._mode_seg: _Segmented | None = None
116        self._diff_seg: _Segmented | None = None
117        self._diff_label: Label | None = None
118        self._root: VBoxContainer | None = None
119        self._enter = 0.0  # title entrance 0 -> 1 (eased), persists across resizes
120
121    def on_ready(self):
122        self._rebuild()
123        self.tree.screen_resized.connect(self._on_resize)
124        self.start_coroutine(tween(self, "_enter", 1.0, 0.55, easing=ease_out_back))
125
126    def on_update(self, dt):
127        self.queue_redraw()  # drawn title + living backdrop animate every frame
128
129    def _on_resize(self, *_):
130        if self._root is not None:
131            self._root.destroy()
132            self._root = None
133        self._rebuild()
134
135    def _screen_w(self) -> float:
136        return render.screen_wh(self.tree)[0]
137
138    def _rebuild(self):
139        w = self._screen_w()
140        col = max(280.0, min(w * 0.7, 440.0))
141        root = VBoxContainer(name="MenuCol")
142        root.set_anchor_preset(AnchorPreset.CENTER)
143        root.margin_left = -col / 2
144        root.margin_right = col / 2
145        root.margin_top = -250
146        root.margin_bottom = 250
147        root.size = Vec2(col, 500)
148        root.separation = 12
149
150        # The "BLOOM" title is drawn in on_draw (animated glow + entrance); reserve
151        # its slot here so the column layout is unchanged.
152        root.add_child(self._spacer(col, 90))
153
154        tag = Label("Grow your colour. Starve theirs.")
155        tag.font_size = 15.0
156        tag.text_colour = TEXT_DIM
157        tag.alignment = "center"
158        tag.size = Vec2(col, 22)
159        root.add_child(tag)
160
161        root.add_child(self._spacer(col, 10))
162
163        self._mode_seg = _Segmented([("ai", "vs Computer"), ("hotseat", "2 Players")], self.mode, self._set_mode)
164        root.add_child(self._mode_seg.build(col, 46))
165
166        self._diff_seg = _Segmented(
167            [("calm", "Calm"), ("sharp", "Sharp"), ("ruthless", "Ruthless")], self.difficulty, self._set_difficulty
168        )
169        root.add_child(self._diff_seg.build(col, 46))
170
171        self._diff_label = Label(DIFFICULTY_BLURB[self.difficulty])
172        self._diff_label.font_size = 13.0
173        self._diff_label.text_colour = TEXT_DIM
174        self._diff_label.alignment = "center"
175        self._diff_label.size = Vec2(col, 20)
176        root.add_child(self._diff_label)
177
178        root.add_child(self._spacer(col, 8))
179
180        play = make_button(
181            "PLAY", col, 58, font=24.0, bg=PLAY_BG, hover=PLAY_HOVER, down=(0.50, 0.27, 0.12, 1.0), fg=(1, 1, 1, 1)
182        )
183        play.pressed.connect(self._on_play)
184        root.add_child(play)
185
186        how = make_button("How to play", col, 40, font=15.0)
187        how.pressed.connect(self.help_requested.emit)
188        root.add_child(how)
189
190        if self._show_quit:
191            quit_btn = make_button("Quit", col, 36, font=14.0, fg=TEXT_DIM)
192            quit_btn.pressed.connect(self.quit_requested.emit)
193            root.add_child(quit_btn)
194
195        self._apply_mode_state()
196        self.add_child(root)
197        self._root = root
198
199    def _spacer(self, w, h) -> Label:
200        s = Label("")
201        s.size = Vec2(w, h)
202        return s
203
204    def _set_mode(self, mode):
205        self.mode = mode
206        self._apply_mode_state()
207
208    def _set_difficulty(self, diff):
209        self.difficulty = diff
210        if self._diff_label:
211            self._diff_label.text = DIFFICULTY_BLURB[diff]
212
213    def _apply_mode_state(self):
214        if self._diff_seg:
215            self._diff_seg.set_enabled(self.mode == "ai")
216
217    def _on_play(self):
218        self.start_requested.emit({"mode": self.mode, "difficulty": self.difficulty})
219
220    def on_draw(self, renderer):
221        w, h = render.screen_wh(self.tree)
222        now = self.tree.now
223        # living backdrop: a few big, faint, slowly breathing blooms behind the menu
224        blooms = (
225            (w * 0.16, h * 0.32, render.WARM, 0.0),
226            (w * 0.85, h * 0.62, render.COOL, 2.1),
227            (w * 0.52, h * 0.9, render.WARM, 4.0),
228        )
229        plen = min(w, h) * 0.13
230        for bx, by, col, ph in blooms:
231            breath = 1.0 + 0.10 * math.sin(now * 0.5 + ph)
232            render.draw_flower(renderer, bx, by, plen * breath, (col[0], col[1], col[2], 0.04))
233        # the hero title: entrance scale/fade + gentle breathing + a soft warm glow
234        e = max(0.0, min(1.0, self._enter))
235        cx, cy = w / 2.0, h / 2.0 - 205.0
236        px = max(54.0, min(w * 0.10, 96.0))
237        breath = 1.0 + 0.02 * math.sin(now * 1.7)
238        sc = (px / 16.0) * (0.9 + 0.1 * self._enter) * breath
239        # soft 3-ring additive aura (falloff, not a hard orb)
240        glow = (1.0, 0.66, 0.34)
241        gb = 1.0 + 0.05 * math.sin(now * 1.7)
242        for gr, ga in ((0.55, 0.14), (0.95, 0.08), (1.4, 0.04)):
243            render.draw_disc(renderer, cx, cy, px * gr * gb, (glow[0], glow[1], glow[2], ga * e))
244        renderer.draw_text(
245            "BLOOM",
246            rect=(0, cy - px, w, 2 * px),
247            colour=(TITLE[0], TITLE[1], TITLE[2], e),
248            scale=sc,
249            alignment="centre",
250            vertical_alignment="centre",
251            screen_space=True,
252        )
253
254
255class HowToCard(Node2D):
256    """Looping live demo of the one rule plus a legend. Reuses the real engine."""
257
258    back_requested = Signal()
259
260    def __init__(self, **kw):
261        super().__init__(name="HowToCard", **kw)
262        self.board = Board(radius=1)
263        self.view = HexBoardView()
264
265    def on_ready(self):
266        self._reset_demo()
267        self.start_coroutine(self._loop())
268        back = make_button("Back", font=18.0)
269        back.set_anchor_preset(AnchorPreset.CENTER_BOTTOM)
270        back.margin_left = -150
271        back.margin_right = 150
272        back.margin_top = -64
273        back.margin_bottom = -16
274        back.size = Vec2(300, 48)
275        back.pressed.connect(self.back_requested.emit)
276        self.add_child(back)
277
278    def on_update(self, dt):
279        self.queue_redraw()  # dynamic on_draw (animated demo + non-Property text)
280
281    def _reset_demo(self):
282        self.board.cells = dict.fromkeys(hex_in_range(CENTRE, 1), Owner.EMPTY)
283        self.board.cells[CENTRE] = Owner.COOL
284        self.board.cells[Hex(1, 0)] = Owner.WARM
285        self.board.last_placed = None
286        self.view.sync_static(self.board)
287
288    def _loop(self):
289        while True:
290            yield from wait(1.1)
291            seed = Hex(-1, 1)
292            if self.board.cells.get(seed) is Owner.EMPTY:
293                resolve_placement(self.board, seed, Owner.WARM)
294                self.view.sync_static(self.board)
295            yield from wait(1.6)
296            self._reset_demo()
297
298    def on_draw(self, renderer):
299        w, h = render.screen_wh(self.tree)
300        renderer.draw_rect((0, 0), (w, h), colour=(0.04, 0.05, 0.07, 0.82), filled=True, screen_space=True)
301        renderer.draw_text(
302            "How to play", (w / 2, h * 0.10), colour=TITLE, scale=1.8, alignment="centre", screen_space=True
303        )
304        dh = min(w * 0.6, h * 0.32)
305        demo_top = h * 0.17
306        self.view.layout(self.board, w / 2 - dh / 2, demo_top, dh, dh)
307        self.view.draw(renderer, self.board)
308        # legend row, centred under the demo
309        ly = demo_top + dh + 26
310        self._legend(renderer, w / 2 - 150, ly, render.WARM, "You")
311        self._legend(renderer, w / 2 - 24, ly, render.COOL, "Opponent")
312        self._legend(renderer, w / 2 + 118, ly, render.EMPTY, "Empty")
313        # rule text, width-fitted so it never overflows on phones
314        rules = [
315            ("Press an empty hex to aim; release to place a seed.", TEXT, 0.95),
316            ("Capture rule: count only the cells TOUCHING a hex. An enemy hex flips", TEXT_DIM, 0.9),
317            ("when your seeds touching it outnumber its own. Then captures ripple out.", TEXT_DIM, 0.9),
318            ("Your hexes flip the same way on their turn, so keep yours connected.", TEXT_DIM, 0.9),
319        ]
320        for i, (line, colour, scale) in enumerate(rules):
321            renderer.draw_text(
322                line,
323                rect=(20, ly + 26 + i * 22, w - 40, 22),
324                colour=colour,
325                scale=scale,
326                alignment="centre",
327                fit_to_width=True,
328                screen_space=True,
329            )
330
331    def _legend(self, renderer, x, y, colour, text):
332        renderer.draw_polygon([(c.x, c.y) for c in hex_corners(Vec2(x, y), 10)], colour=colour, filled=True)
333        renderer.draw_text(
334            text, (x + 16, y), colour=TEXT, scale=0.8, alignment="left", vertical_alignment="centre", screen_space=True
335        )
Next
render.py
Previous
hex_grid.py
Copyright © 2026, SimVX Contributors
Made with Sphinx and @pradyunsg's Furo