5.7 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 |
smash_items |
assets/sprites/smash-items.png |
64x64 x 6 frames | 1x6 spritesheet: intact/smashed TV, chair, cone (see Smashables below) |
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.SMASH_ITEM.*- smashable prop physics (launch speed/angle/spin, density, etc. - see the block in config.js for the full list).SMASH_ITEM.launchSpeedandSMASH_ITEM.launchAngleare the two knobs that matter most for "how far and how high the item flies when the bus hits it."
Set DEBUG = true in config.js to show a live g-force readout and Matter's
physics debug overlay in PlayScene.
Smashables (TV / chair / cone)
assets/sprites/smash-items.png is a 1x6 spritesheet (TV, smashed TV, chair,
smashed chair, cone, smashed cone), loaded as the smash_items texture
(64x64 per frame). Any level can carry an optional items array
(see the editor's export for the exact shape):
items: [
{ type: 'tv', mode: 'float', x: 2500, y: 660 },
{ type: 'chair', mode: 'gravity', x: 3500, y: 760 },
]
mode: 'float'- the item rests exactly at(x, y)(a backdrop prop, static body, never moves until smashed).mode: 'gravity'- the item spawns at(x, y)but is a dynamic body, so it drops and settles onto the ground under that X at level start.
The first time the bus (chassis or either wheel) touches an intact item it
swaps to its smashed frame and launches up-and-forward relative to the bus's
travel direction, tumbling, on the world's normal gravity. It lands wherever
it lands and stays. SMASH_ITEM in config.js holds the launch physics.
Smashed items also tally as +SCORE.perSmashItem (50) each on the
end-of-level score screen.
The level editor (editor.html) has a "Smashables" section: pick a type
- mode, then click anywhere on the preview map to drop one there; click an
existing item (on the map or in the list) to remove it. Exporting the level
writes the
itemsarray into the level file automatically.
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).