Making a game
Bringing in art
Pictures, sheets, models and fonts into a game and drawn.
Every file a game draws is an entry in its scene's catalogue, world.loader.assets, fetched and decoded by the loader. Code names the entry, never the file's path.
The types
| Type | For |
|---|---|
json | A data file a rule reads, the definitions among them. |
sheet | An Aseprite or TexturePacker sheet, or a Tiled .tsj: frames and clips. |
texture | One picture, decoded by the scene's renderer. |
model | One self-contained .glb, under render3d. |
bitmapfont | An AngelCode .fnt and its pages. |
palette | A GIMP .gpl. |
pixels, binary | A PNG or bytes a rule reads as data. |
Steps
- Put the file in
games/<game>/assets/, with the script that draws, writes or cuts it kept beside it so it can be made again. - A model or texture the game ships is built, never raw: a glb packed by gltfpack, with meshopt geometry and WebP textures, and a loose texture as a
.webp. The raw parts it is made from, source glbs, full-size texture sets and packs, live in the library thatPATCHWORK_LIBRARYnames, never in the game. The game'sgames/<game>/assets/<game>.bake.tsmakes every built file from the library and the scripts, andyarn bake <game>runs it onceyarn healthhas checked the machine. Node and material names, extras and float positions survive the packing, but a glTF mesh's own name does not, so a mesh found by name is a node of one primitive. The loader reads both with no setup. - Write its entry:
type, aurlfrom the repository root with a leading/,preloadwhen a rule reads it on its first step, and the type's own fields (nearestfor pixel art on a sheet,facing,palette,for, andspace,wrapandanisotropyon a 3D texture). - Give a sheet's frames a
pivot, the point that stands on the row'spos; a frame with none stands on its top-left corner. - Name the entry where its drawer reads it: the kind's definition in
assets/<game>.json, a plugin's table onworld, or the row when it changes. - Draw it with a plugin, or with the kind's own
<kind>.client.ts(below). - For a picture the HUD shows, add a second entry with the same
urland"for": "ui".
"loader": { "assets": {
"hero": { "type": "sheet", "url": "/games/<game>/assets/hero.json", "preload": true, "nearest": true },
"coin.ui": { "type": "texture", "url": "/games/<game>/assets/coin.png", "for": "ui", "preload": true }
} }
Drawing with a plugin
A kind listed in a plugin's table needs no <kind>.client.ts.
- sprite2d:
world.sprite2d.kinds.<kind>, frames of a sheet in render2d's world. The kind's rule movesframeon withplay. - sprite3d: billboards of a sheet in render3d.
- anim:
world.anim.kinds.<kind>, a model posed by clips from a rig file. - particle2d and particle3d: sheets named in an effects file.
Drawing with the kind's own look
- In
setup, make the sprite or mesh and keep it in a variable at the top of the file. - In
tick, askplugins.loader.get(name), andplugins.loader.load(name)while it hands back nothing. Size a PixiJS sprite withwidthandheight. - For a model, call
plugins.loader.model(name)once; each call hands back its own copy. - In
remove, destroy the sprite or mesh and leave the texture, which is the loader's.
Checking it
- Test the look in
<kind>.client.test.ts: the url asked for against the literal path, the size and anchor from the definition. - Open the game's
index.htmlas in Starting a game. The picture is drawn where its row says, at its size, sharp where it should be, and a clip plays at its pace. - A blank page is a preloaded data file that failed, so the scene never started; missing art on a running page is a picture, model or font that failed. The browser's console names the entry and why, as
loader <name>: ….