Files
endless-runner-vibe/README.md
Gabriel Kaszewski 951bd51f12 Add WebAssembly build support via Emscripten
- Cargo.toml: gate `wayland` feature behind cfg(not(wasm32)) so the
  dependency resolves correctly for both native and web targets
- .cargo/config.toml: WASM linker flags (ASYNCIFY, GLFW, memory growth)
- build_web.sh: sets required EMCC_CFLAGS, builds, copies artefacts to web/;
  accepts --serve to start a local HTTP server
- web/index.html: minimal Emscripten shell with canvas and status line
- .gitignore: exclude generated web/*.js and web/*.wasm artefacts
- README: document web build prerequisites and usage

ASYNCIFY lets the native while-loop main loop run unchanged in the browser.
Build output: ~267 KB JS + ~458 KB WASM.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 00:35:21 +01:00

3.1 KiB
Raw Blame History

Endless Runner

A 2D side-scrolling endless runner built with Rust and raylib.

Rust

Gameplay

Run as far as you can on procedurally generated platforms. The world scrolls faster the longer you survive. Stomp enemies for bonus points, collect power-ups, and don't fall off the screen.

Controls

Action Keys
Jump Space / W /
Restart Space / R / Enter

Power-ups

Icon Name Effect
I Invincible Immune to side hits for 5 s
J Jump Boost 1.45× jump height for 6 s
2 2× Score Double score gain for 8 s

Scoring

  • Passive: 80 pts/s (multiplied by any active score effect)
  • Stomp: 100 pt bonus per enemy (also multiplied)

Building

Prerequisites: Rust toolchain, raylib system library, a Wayland compositor.

# debug
cargo run

# release (LTO + stripped)
cargo run --release

Web (WebAssembly)

Prerequisites: Emscripten SDK activated in your shell.

./build_web.sh           # produces web/*.js + web/*.wasm
./build_web.sh --serve   # build then serve at http://localhost:8080

The web build uses ASYNCIFY so the main game loop requires no changes between native and browser targets.

Tests

cargo test

42 unit tests covering physics, collision, effects, and level generation.

Architecture

src/
  main.rs       — entry point, 60 fps cap, dt capped at 0.05 s
  config.rs     — Config struct (all tunable parameters)
  player.rs     — vertical physics, coyote-time jump window
  platform.rs   — scrolling platforms
  enemy.rs      — scrolling enemies with stomp detection
  pickup.rs     — PickupDef (data) + in-world Pickup instance
  effects.rs    — ActiveEffect trait + three concrete impls
  level_gen.rs  — procedural generation (StdRng, scrolling frontier)
  world.rs      — simulation loop, collision resolution, rendering
  game.rs       — state machine (Playing / GameOver), HUD

Key design decisions

  • Player x is fixed; only the world scrolls.
  • Speed follows initial + factor × ln(1 + elapsed / time_scale) — fast early ramp, logarithmic plateau.
  • Landing detection uses the previous and current bottom-y crossing the platform surface, making it tunnelling-safe at high speeds.
  • Coyote time: the jump window stays open for 120 ms after leaving a platform edge.
  • Power-ups are fully data-driven via Config::pickup_defs; adding a new type requires one PickupDef entry and one ActiveEffect impl.

Tuning

All gameplay parameters live in Config::default() in src/config.rs — no recompilation of other modules needed when tweaking values.

initial_speed: 280.0,       // px/s at t=0
speed_log_factor: 150.0,    // how much speed grows
speed_time_scale: 30.0,     // seconds before growth slows
gravity: 1900.0,            // px/s²
jump_velocity: -800.0,      // px/s (negative = up)
coyote_time: 0.12,          // seconds
enemy_spawn_chance: 0.35,   // per platform
pickup_spawn_chance: 0.28,  // per platform