Making a game

Starting a game

From yarn new-game to the first thing of its own playing.

This page covers a new game from yarn new-game to its first kind of its own playing in one tab and against its server.

Making the folder

  1. From the repo's root, run yarn new-game <name>. It writes games/<name>/ with one scene, main (play for a game named main), 960 by 540 pixels, on its own port. The name is a lower-case letter followed by lower-case letters, digits and underscores, and may not be start, player, body, a plugin's or addon's name, or an existing game. It refuses any other name and writes nothing.
  2. Name the game after the game, never after a kind it will have: <game>.sim.ts is the manager's file, so a kind with the game's name would collide with it.
FileWhat it is
<game>.scene.jsonThe manager scene: the game's scenes, the entry scene every connection enters, the port, and the step dt. Its world and prefabs sit under every scene's own, so a value every scene uses is written once here.
<game>.sim.tsThe manager's code, which only lists scenemanager.
main.scene.json, main.sim.tsThe scene on loader, scenemanager, player and render2d, and the list of every kind it makes rows of.
start.sim.tsWhere a seat starts. No rule and no drawing.
player.sim.ts, player.client.tsThe row a seat plays as, and its arrow keys.
body.sim.ts, body.client.tsA square on a row of its own, which the next step folds into the player row.
assets/<game>.jsonThe definitions: each kind's fixed numbers.
index.html, client.html, <game>.server.tsThe game in one tab; one seat over a socket; the server on node.

Folding the body into the player row

The thing a seat plays as is its own player row (Designing a seat), so the template's separate body row is moved onto it first.

  1. In main.scene.json, give the player prefab "seat": 0, "placed": false, "pos": [0, 0], "move": [0, 0] and delete the body prefab. Add world.predict: { "epsilon": 0.000001, "cutoff": 60, "halflife": 100, "snap": 5, "below": 0.001 }. See predict.
  2. In assets/<game>.json, rename the body entry to player, keeping its speed, size and colour.
  3. Move the body's rule into player.sim.ts: before stands the row on the start of its slot once and sets placed; tick runs it at its definition's speed along move; the move act stores the direction, at most one unit long. Use math's hypot, not Math.hypot.
  4. Move the body's drawing into player.client.ts, drawn for every seat's row, and bind the arrow keys only for the row marked mine.
  5. Delete body.sim.ts and body.client.ts and remove body from main.sim.ts.
  6. Rewrite client.html so its link is wrapped in predict, calling page.input() then await page.frame(now) each frame.

Where the first numbers go

Every number is in the scene file or a data file it names, as Where things go sets out. A rule that rolls w.die() needs a world.seed in its scene. The canvas size is the width and height of the scene's engine prefab. Every file the game reads is an entry of world.loader.assets, with its path from the repo's root.

The first kind of its own

For a coin that jumps somewhere new when a seat's square reaches it:

  1. Its prefab, the fields that change: prefabs.coin: { "pos": [0, 0], "taken": 0 }.
  2. Its actors entry: { "is": "coin", "pos": [640, 270] }.
  3. Its definition in assets/<game>.json: { "reach": 24, "size": 16, "colour": "#5ad1e8", "bounds": [[16, 16], [944, 524]] }.
  4. Its rule, coin.sim.ts, and its drawing, coin.client.ts, both reading fixed numbers off the definition.
  5. Its name in main.sim.ts's sim, and a world.seed if its rule rolls the die.

Every seat sees it with nothing more. More in Designing a kind.

The testbed

Copy index.html to dev.html to try things: enter any scene, override any value of a scene, prefab or definition in the page's copy before the boot, and add readouts. Nothing else reads it, and what it finds goes into the scene or data files.

Checking it

  1. Run yarn editor <game> and open localhost:5173/games/<game>/index.html. The square moves with the arrow keys.
  2. From the repo's root, run node --experimental-strip-types games/<game>/<game>.server.ts and open client.html. The square moves on the frame after the key goes down.
  3. A second seat needs the scene's seats raised and a start entry for slot 1, then a second client.html tab. A second tab of index.html is a second game.
  4. Run the game's test from the repo's root: yarn vitest run games/<game>/<game>.sim.test.ts.