Skip to content

Add a checkpoint and kill zone ​

Goal ​

Copy templates/blank. Enter a checkpoint, then a kill box, and respawn at the checkpoint with velocity cleared. No custom respawn system is needed.

Files you will edit ​

  • src/game.ts
  • src/level.json

Steps ​

  1. Create src/level.json. The checkpoint is a box with fixed [1,1,1] half extents; its root position and yaw are the respawn pose. The kill volume can have custom half extents.
json
{
  "format": 1,
  "actors": [
    { "class": "LevelSettings", "position": [0, 0, 0], "props": { "killHeight": -10 } },
    {
      "class": "PlayerStart",
      "position": [0, 1.2, 0]
    },
    {
      "class": "MeshActor",
      "position": [0, -0.5, 0],
      "props": {
        "asset": "cube-muted",
        "scale": [20, 1, 20],
        "collision": "block",
        "halfExtents": [10, 0.5, 10]
      }
    },
    {
      "class": "CheckpointVolume",
      "name": "checkpoint",
      "position": [0, 1.2, -3],
      "yaw": 90,
      "props": {
        "order": 1,
        "once": true
      }
    },
    {
      "class": "KillVolume",
      "name": "pit",
      "position": [0, 1.2, -7],
      "props": {
        "halfExtents": [2, 2, 0.5]
      }
    }
  ]
}
  1. Replace src/game.ts. player.spawn: 'playerStart' waits until level and init actors exist before selecting a start. LevelSettings.killHeight adds a global fallback for falling off the floor; null = auto (50 m under the level), false = off.
ts
import {
  defineGame,
  prefab,
  PlayerStart,
  LevelSettings,
  MeshActor,
  validateLevel,
  CheckpointVolume,
  KillVolume,
  type GameContext,
} from 'gameable';
import settings from '../settings.json';
import data from './level.json';

// Register the level's classes in the guest bundle.
void PlayerStart;
void LevelSettings;
void MeshActor;
void CheckpointVolume;
void KillVolume;
const Hero = prefab({
  asset: 'cube-mint',
  scale: [0.6, 1.8, 0.6],
  body: { shape: 'capsule', dims: [0.4, 0.5], kind: 'character' },
});
function walk(ctx: GameContext): void {
  const move = ctx.input.axis2('KeyA', 'KeyD', 'KeyS', 'KeyW');
  ctx.physics.moveCharacter(ctx.player, move.x * 4, 0, -move.y * 4);
}

export default defineGame({
  features: settings.features,
  world: settings.world,
  assets: ['cube-mint', 'cube-muted'],
  level: validateLevel(data),
  player: { prefab: Hero, spawn: 'playerStart', camera: 'thirdPerson' },
  systems: [walk],
});
  1. Add later CheckpointVolume entries with increasing order. A lower order cannot replace a higher checkpoint. once remembers use per seat. KillHeight respawns players; non-player actors entering its box require overlaps: true and are destroyed with reason killHeight. The global height plane does not require that opt-in.

Verify ​

From the copied game root run npx tsc --noEmit -p tsconfig.json, then npm run build (in this checkout use npm run build -w templates/blank). Blank is a bundle template, with no dev-server script: open the resulting bundle in the common Gameable player used by your app (examples/play/README.md describes local hosting).

Check it: hold W to pass through the checkpoint at Z=-3, then enter the kill box at Z=-7. The player returns to [0,1.2,-3] before settling onto the floor. A snapshot/restore preserves that checkpoint. Reload and enter the kill box by going around the checkpoint: respawn uses PlayerStart instead.

See also ​