HeartBeast Action RPG: SimVX Port
A SimVX re-implementation of HeartBeast's Action RPG tutorial, whose canonical
source is the Action RPG folder of
uheartbeast/youtube-tutorials.
What it is
The complete combat loop of the tutorial: movement with acceleration and friction, a sword that swings in the facing direction, a roll that dashes and grants invincibility frames, bats driven by an Idle to Wander to Chase state machine, soft collision so bats push each other apart instead of stacking, destructible grass, and a heart HUD.
No upstream art is redistributed (see ATTRIBUTION.md), so every visual is
drawn at runtime with the immediate-mode 2D API.
Running
uv run python examples/ports/heartbeast_rpg/main.py # desktop
uv run python examples/ports/heartbeast_rpg/main.py --test # headless smoke run
Controls
| Action | Keyboard | Pointer / touch |
|---|---|---|
| Start | Space or Enter | Tap anywhere |
| Move | W/A/S/D or the arrow keys | Drag from the lower-left half |
| Attack | J or Z | Tap ATK |
| Roll / dodge | K or X | Tap ROLL |
| Quit | Escape | -- |
Architecture
nodes/
├── effects.py HitEffect, EnemyDeathEffect, GrassEffect (self-destructing)
├── enemy.py Bat: Idle/Wander/Chase state machine plus soft collision
├── grass.py Destructible grass tufts
├── hud.py Heart health bar and the bottom controls strip
├── player.py Movement physics, sword, roll, invincibility frames
├── stats.py Signal-based health tracker (mirrors the upstream Stats scene)
├── ui.py Title card and the on-screen stick / action buttons
└── world.py Playfield, camera, decorations, collision dispatcher
What it shows off in SimVX
- Signals carry health changes and effect spawns:
Statsemitshealth_changed/no_health, the bats emit hit and death signals that the world turns into particle bursts, and the HUD only ever listens. - Camera2D does the following and the clamping: the port sets
target,zoomand the fourlimit_*properties from the window size, rather than reimplementing them. - CanvasLayer keeps the HUD, the title card and the pointer controls in screen space above a camera-transformed world.
- The retained 2D contract: nodes that animate every frame declare
dynamic, and nodes that change on an event (the hearts) callqueue_redraw()instead. - Procedural 2D drawing throughout:
draw_rect,draw_circle,draw_polygonanddraw_text, with no textures at all.
Feature parity with upstream
| Feature | Status | Notes |
|---|---|---|
| Player movement (accel/friction) | Yes | Matches the upstream speed values |
| 8-directional movement | Yes | Normalised diagonals |
| Sword attack with rotation | Yes | Hitbox swings along the facing |
| Roll/dodge with i-frames | Yes | 0.3s, 250 px/s dash |
| Enemy AI (Idle/Wander/Chase) | Yes | Detection radius of 120 px |
| Soft collision | Yes | Push-apart between overlapping bats |
| Destructible grass | Yes | 80 tufts, leaf burst on destruction |
| Invincibility frames plus blink | Yes | Player and bats both blink |
| Health system (signals) | Yes | Stats node with health_changed / no_health |
| Heart HUD | Yes | 6 hearts, updated from signals |
| Bounded camera | Yes | Camera2D target plus limits, clamped to the map |
| Death/respawn | Yes | 2s delay, then full health |
| Hit/death/grass effects | Yes | Fading particle bursts that self-destruct |
| Tree/bush decorations | Yes | Drawn procedurally |
| Terrain | Partial | A drawn ground rect and a polygon path, not a TileMap |
| Sprite animation | Partial | Wing flap is procedural, no sprite sheets |
| Audio | No | Upstream SFX are not redistributable |
Web export
uv run simvx export web examples/ports/heartbeast_rpg/main.py -o /tmp/heartbeast_rpg.html
The exporter auto-detects GameRoot; no extra flags are needed.
Licence
The upstream tutorial code is MIT (Copyright (c) 2020 Heart GameDev); the
notice is preserved in UPSTREAM_LICENSE.md. The upstream pixel art and audio
are not covered by that licence and are not bundled here. See
ATTRIBUTION.md for the full provenance.