3.9 KiB
Monsterplex
A Trials-style physics driving game starring a school bus, crossed with Snuggle Truck: land badly enough and the g-force throws your passengers off. Built with Phaser 4, plain ES6 modules, and no package manager.
Running it
Browsers block ES module imports over file://, so serve the folder with any
static file server. No install needed - this ships with Python 3:
python3 -m http.server 8000
Then open http://localhost:8000/.
Controls
- Arrow Up / W - throttle
- Arrow Down / S - brake / reverse
- Arrow Left/Right / A/D - lean the bus left/right
Land smoothly or the g-force throws a kid off the bus. Lose all of them and the level fails.
Project layout
index.html entry point, import map -> vendor/phaser.esm.js
vendor/phaser.esm.js vendored local copy of Phaser 4 (see below)
src/main.js Phaser.Game config + scene list
src/config.js every tunable constant (physics, g-force threshold, camera, ...)
src/scenes/ Boot -> Preload -> Intro -> MainMenu -> LevelSelect (map) -> Play -> LevelScore -> LevelSelect / LevelFailed
src/entities/ Bus, Kid, Terrain
src/systems/ GForceMonitor, KidManager, InputController, CameraRig
src/data/levels/ the hand-authored levels (10, split across the two campaigns)
src/util/ asset manifest, placeholder-texture generator, localStorage progress, small UI helper
assets/ where real art goes (see manifest below) - currently empty
Adding real art
No art assets exist yet - src/util/assetManifest.js lists every image the
game expects, and PreloadScene falls back to a synthesized colored
placeholder (via src/util/placeholderTextures.js) for anything missing.
Drop a real PNG at the exact path below and it's picked up automatically -
no code changes needed.
| key | path | size | notes |
|---|---|---|---|
bus_chassis |
assets/sprites/bus_chassis.png |
170x64 | scaled to this size regardless of source resolution |
bus_wheel |
assets/sprites/bus_wheel.png |
52x52 | used for both wheels |
kid_idle |
assets/sprites/kid_idle.png |
28x28 | passenger while aboard |
kid_ejected |
assets/sprites/kid_ejected.png |
28x28 | passenger after being thrown off |
bg_far |
assets/backgrounds/bg_far.png |
256x540 | tiled, slowest parallax layer |
bg_mid |
assets/backgrounds/bg_mid.png |
256x540 | tiled, mid parallax layer |
bg_near |
assets/backgrounds/bg_near.png |
256x540 | tiled, fastest parallax layer |
icon_kid |
assets/ui/icon_kid.png |
24x24 | HUD "kids aboard" icon |
favicon |
assets/favicon.png |
32x32 | browser tab icon |
Tuning the feel
Everything worth tweaking lives in src/config.js, notably:
GFORCE.ejectThresholdG- how hard a landing has to be before a kid is thrown off. Not physically derived - tune by playtesting.GFORCE.gracePeriodMs/ejectCooldownMs- ignore-window after level start/restart, and minimum time between two ejections so one bad landing doesn't empty the whole bus at once.BUS.*- suspension stiffness/damping, wheel friction, throttle/lean torque and their clamps.
Set DEBUG = true in config.js to show a live g-force readout and Matter's
physics debug overlay in PlayScene.
Phaser 4 loading
Phaser is vendored locally at vendor/phaser.esm.js (downloaded once from
jsDelivr's CDN build) and wired up via an import map in index.html, so
every source file just does import Phaser from 'phaser' like a normal npm
project would - the only place that knows about the vendored file is that one
import map line. This keeps the project working fully offline with no
runtime dependency on a third-party CDN staying up.
To upgrade Phaser later: download a newer phaser.esm.js build over
vendor/phaser.esm.js and re-verify Bus.js against the Matter physics
Factory API in the new file (grep for class Factory under the Matter
section).