monsterplex/sprites.md

176 lines
10 KiB
Markdown

# Adding real art
Nothing in the code needs to change to swap in real art. `src/util/assetManifest.js`
lists every image the game expects, with an exact `path`. `PreloadScene` tries to
load each one; if the file doesn't exist (or fails to load), it falls back to a
synthesized colored placeholder shape instead. **Drop a real PNG at the exact path
below, refresh the page, and it's used automatically.**
## Where files go
All paths are relative to the project root (`/home/brianfertig/git/monsterplex/`).
The folders already exist and are currently empty.
| key | path | size used in-game | shape | notes |
|---|---|---|---|---|
| `bus_chassis` | `assets/sprites/bus_chassis.png` | 340 x 128 | rectangle | see "Bus" below |
| `bus_wheel` | `assets/sprites/bus_wheel.png` | 104 x 104 | circle | one image, used for both wheels |
| `kid_idle` | `assets/sprites/kid_idle.png` | 56 x 56 | circle | passenger while aboard the bus |
| `kid_ejected` | `assets/sprites/kid_ejected.png` | 56 x 56 | circle | passenger after being thrown off |
| `bg_far` | `assets/backgrounds/bg_far.png` | tiled, 512 x 1080 native | - | slowest-scrolling parallax layer (sky/distant) |
| `bg_mid` | `assets/backgrounds/bg_mid.png` | tiled, 512 x 1080 native | - | mid-speed parallax layer |
| `bg_near` | `assets/backgrounds/bg_near.png` | tiled, 512 x 1080 native | - | fastest-scrolling parallax layer (closest) |
| `icon_kid` | `assets/ui/icon_kid.png` | 48 x 48 | - | small HUD "kids aboard" icon, top-left |
| `favicon` | `assets/favicon.png` | 32 x 32 | - | browser tab icon - see caveat below |
"Size used in-game" is the size the game actually displays the image at
(`setDisplaySize`), **not** a requirement on the source file's resolution - see
"Resolution" below.
These numbers are the game's current 1920x1080 resolution (`WORLD_SCALE = 2`
in `src/config.js`) - if that changes again, every size in this table changes
with it (same ratios, just re-derive from that one constant).
## Bus
- `bus_chassis.png` and `bus_wheel.png`, transparent background (PNG alpha).
- **The bus always faces right** (nose/front toward positive x - the direction
it drives at level start) and there's no left/right flip when reversing, so
draw the chassis facing right.
- The chassis's rectangle *is* the physics hitbox (with rounded corners baked
in by the physics engine, not the art). Keep the bus's silhouette roughly
filling a 340x128 box so the visual matches where it actually collides.
- `bus_wheel.png` is used for **both** wheels (front and rear) - same image,
no separate left/right variant needed. It's a real physics body, so it
visually spins as the bus drives; a plain hubcap/circle reads better at
speed than fine detail.
- Campaign 2 uses its own wheel image, `bus_wheel_2.png` (bigger - it's
displayed at a ~1.2x radius around the same axle center, selected via the
`bus` block in `src/data/themes.js`), its own chassis image,
`bus_chassis_2.png` (same 340x128 design box, physics body unchanged -
only the texture is swapped per campaign), and a "lift kit": the wheel
mount point sits ~8 design units lower under the chassis (see
`wheelRestOffsetY2` in `src/config.js` and the `bus` block in
`src/data/themes.js`), so it reads as a lifted bus. Both wheels still
share one image per campaign.
- If you want a bus with different proportions (longer, taller, etc.) than
340x128, tell me the size you want art at - that number is also the physics
body size in `src/config.js` (`BUS.chassisWidth`/`chassisHeight`), so it
needs a matching code change, not just a differently-shaped image.
## Kids
- `kid_idle.png` (aboard) and `kid_ejected.png` (mid-air after being thrown
off) - two separate images, swapped automatically when a kid is ejected.
- Physics shape is a circle, but the *image* itself renders as a full square
at 56x56 - draw the character centered in a square canvas with transparent
padding around them rather than filling every corner, or they'll look like
they're poking out past their own collision circle.
- `kid_ejected` is a good place to show some "yikes" energy (arms out,
startled face, etc.) since it only appears for the ~2 seconds after a kid
gets thrown clear.
## Parallax backgrounds
- `bg_far` / `bg_mid` / `bg_near` are rendered as horizontally **tiling**
strips (`TileSprite`), not single stretched images - whatever you supply
repeats sideways as the camera scrolls.
- Make the **left and right edges match** so the seam is invisible when it
tiles (or design something patternable, like scattered clouds/hills on a
transparent background).
- far/mid/near are layered back-to-front and scroll at different speeds for
depth (far moves slowest, near moves fastest) - a plain sky for far, hills
or treeline silhouettes for mid, and closer foreground detail for near
works well.
- Native resolution isn't locked to 512x1080 the way the bus/kid sprites are -
a wider or taller source image just changes how often the tile repeats, so
use whatever gives a clean seamless loop.
## Level select map
The level select screen is a map per campaign: a themed background with a
road ribbon snaking between level nodes, a finish flag past the last node,
and a banner plate behind the campaign title. There is one of each of these
per campaign, plus the shared node/flag/plate sprites.
**Important convention for these sprites:** each is displayed at a fixed
display size with origin (0.5, 0.5) at its map position, so **keep the
subject centered in its file** or it will show up shifted. The node badges
and the flag ship as tight 512x512 images (the badge/flag IS the file);
`map_campaign_tag` is a full-screen 1920x1080 frame with the plate centered
near the top. Sizes below are the display sizes the game uses (design
units); the source file's own resolution only affects crispness.
| key | path | subject size (design units) | notes |
|---|---|---|---|
| `campaign01_bg` | `assets/backgrounds/campaign01_bg.png` | full frame 960x540 | Campaign 1 "Sunny Suburbs" background |
| `campaign02_bg` | `assets/backgrounds/campaign02_bg.png` | full frame 960x540 | Campaign 2 "Dusk Junction" background |
| `map_node` | `assets/ui/map_node.png` | ~58x58 badge (512x512 file) | completed level node |
| `map_node_current` | `assets/ui/map_node_current.png` | ~71x71 badge (512x512 file) | the next level to play ("start here" node) |
| `map_node_locked` | `assets/ui/map_node_locked.png` | ~58x58 badge (512x512 file) | locked level node |
| `map_finish` | `assets/ui/map_finish.png` | ~345x345 (512x512 file) | flag past the last node |
| `map_campaign_tag` | `assets/ui/map_campaign_tag.png` | ~1440x300 plate centered near top | banner behind the campaign title text (title is drawn on top at y≈82/540) |
Per-campaign notes:
- **`campaign0X_bg.png`** - full 960x540 (or 2x) scene, no transparency needed.
The top ~150 units are behind the title plate, so keep that area fairly
quiet. Node positions are auto-placed in a **2-lane serpentine** that sits
in the band **y ≈ 268-412** (design units) across the full map width
(x ≈ 90-870) - both lanes - so keep that horizontal strip fairly open and
readable, and leave the bottom ~60 units clear for the `< Menu` / hint
chrome. The exact lane y-values live in `src/data/levels/positioning.js`
(`LANE_TOP`/`LANE_BOTTOM`) and per-campaign overrides in
`src/data/levels/index.js` (`positions`) - check where your nodes will
land before you paint.
- **Node states** share one silhouette (same size/placement) in three moods:
`map_node` (cleared), `map_node_current` (the one to play next - this one
gets a gentle idle pulse), `map_node_locked` (greyed out, e.g. a padlock).
All three ship as 512x512 badge images with no padding frame; their
display size and hit area are driven by `NODE_ART_FRAMES` +
`BADGE_FILE_FRAC` per-key in `src/util/mapArt.js`, so a different frame
size there is fine as long as those tables are updated. If you want the
badge to be a different display size than ~58-71 units, adjust
`BADGE_FRAC_OF_FRAME` / the per-state `BADGE_FILE_FRAC` entries in
`src/util/mapArt.js` (the click area follows the art, not these knobs).
- **Cleared nodes show the player's best score** under the badge, as a small
gold pill (the game's `SCORE_PILL_STYLE` in `src/scenes/LevelSelectScene.js`)
with the number rolling up from 0 on entrance. The pill is drawn by the
game, not part of the badge art - the `map_node` badge should stay a plain
cleared marker (no digits baked in). The score itself comes from
`bestScore` in `src/util/progress.js` (localStorage), recorded by the
tally scene after each run.
- **`map_finish.png`** - the flag sits just past the last node, so a base/mound
that grounds it looks best. Currently ships as a tight 512x512 image (flag
ink is ~60% of the width, full height); the scene displays it at 0.18x the
game screen, so keep the flag roughly centered in a square frame if you
re-export it. The auto-layout circuit ends at the bottom-right, so the
flag lands in the bottom-right corner past the final node (with custom
`positions` the nudge flips to stay on-screen when the last node is at an
edge).
- **`map_campaign_tag.png`** - a plate/banner with empty center space; the
game writes `"<name> x/y cleared"` on top of it in dark ink (#1a1f29),
so the middle should be a light, uncluttered fill.
## Favicon
- `favicon.png` is the one asset **without** a placeholder fallback for its
actual purpose - the browser tab icon comes from a plain `<link>` tag in
`index.html`, not from Phaser's loader, so until a real file exists at
`assets/favicon.png` the browser just shows its default icon (nothing
broken, just nothing shown).
## Resolution
Supply art at the "size used in-game" from the table above, or a clean
multiple of it (2x/3x) for a crisper look on high-DPI screens - everything
gets scaled to the table's size regardless of the source file's actual pixel
dimensions, so it won't come in stretched or huge, but a very different
aspect ratio than the target box will get squashed/stretched to fit.
## Testing
Just refresh the browser tab after dropping files into `assets/` - no build
step, no server restart needed (unless the static server itself isn't
running yet, in which case see `README.md`).