Making a game

Porting a game

A game that exists elsewhere, brought into Patchwork and proven to play the same.

A port is a new game made in Patchwork with an old one as its specification. Each part of the old game is read for what it does and built again in its home.

Read the old game first

Write down its constants with their units, its state and what changes it, its update in order, what it draws and from which anchor point, its keys, its screens, every Math.random, its timers, its files, what it saves, and its libraries. Measure the frame rate it was played at.

What goes where

Old gamePort
The game loopNot ported: the pages step the manager
The update functionThe before, tick and after of the kind whose state each statement changes, in the old order
The thing the player controls, its score and livesThe seat's player row
State that changesFields of a row, starting values in the prefab
ConstantsThe kind's definition in assets/<game>.json, per second
Canvas sizeprefabs.engine.width and height
Where the player startsA start actor per slot
A loop that lays out the levelRun once, its result written as actors entries
Key handlersThe table player.client.ts hands the player plugin
The draw function<kind>.client.ts, and the ui plugin for text over the game
Images and soundsloader catalogue entries; sounds raised with audio's sound
A title or a next levelA scene
A game-over or pause screenA kind with one actors entry, shown on a ui modal while its row says so
reset()An act that ends the cast and spawns it again from the scene's entries
Math.random()w.die(), with a seed in the scene's world
Math.sin, pow, **The same call on math
A timer or frame counterA field on the row counted down by dt
A physics libraryA physics plugin such as box2d

A speed of 4 pixels a frame, at 60 frames a second, is 240 pixels a second. A canvas rectangle is drawn from its top-left corner, while a row's pos is the thing's middle, so add half the size as each position moves. A kind is never named after the game, a scene or a plugin.

The work in order

  1. Read the old game and write the list above.
  2. Run yarn new-game <name> and its test, yarn vitest run games/<name>/<name>.sim.test.ts. Set the canvas size and place slot 0's start.
  3. Fold the generated body into the player row, and list every kind in main.sim.ts's sim.
  4. Write the numbers: definitions, prefabs, world.seed, the level in assets/main.actors.json, and world.predict.
  5. Write each kind's rule, keeping the old update's order across before, tick and after. Code several kinds share, such as a touch test, goes in a plugin of the game's own.
  6. Bind the keys in player.client.ts.
  7. Write the looks, and the HUD on the ui plugin.
  8. Write the screens and starting over.
  9. Add files and sounds to the catalogue.
  10. Build anything Patchwork lacks in its home first, as Building a plugin describes.
  11. Write the tests beside every file and run yarn tsc.
  12. Run the old game and the port side by side.

Checking it

  • Each kind's <kind>.sim.test.ts, the press test <name>.predict.test.ts, and a replay test under the recorder pass, run one file at a time with yarn vitest run games/<name>/<file>.
  • A script outside the game folder runs the old game headless, with Math.random replaced by the scene's seeded die, and the port frame for frame from the same keys, and prints the first frame where positions, score, lives or state differ. A rule in the wrong pass shows here.
  • Both are played in Chrome with the same keys, and the port on client.html against its server.
  • Every difference found is the game's maker's to accept or fix. A fixed seed means every run deals the same rolls, where the old game varied.