- 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>
100 lines
3.1 KiB
Markdown
100 lines
3.1 KiB
Markdown
# Endless Runner
|
||
|
||
A 2D side-scrolling endless runner built with Rust and [raylib](https://www.raylib.com/).
|
||
|
||

|
||
|
||
## 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.
|
||
|
||
```sh
|
||
# debug
|
||
cargo run
|
||
|
||
# release (LTO + stripped)
|
||
cargo run --release
|
||
```
|
||
|
||
**Web (WebAssembly)**
|
||
|
||
Prerequisites: [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) activated in your shell.
|
||
|
||
```sh
|
||
./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**
|
||
|
||
```sh
|
||
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.
|
||
|
||
```rust
|
||
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
|
||
```
|