Skip to content

Change the level ​

Goal ​

The game renders your own gaussian-splat environment, collides with your own collision mesh, and spawns the player, the enemies and the pickups where you want them.

Files you will edit ​

  • src/assets.json
  • src/level.json

Steps ​

  1. Point the manifest at your world. Drop the files in public/ — Vite copies that directory verbatim — and give them ids. The id is the contract; game code never sees a path.

    diff
    // src/assets.json
     {
       "version": 1,
       "baseUrl": "",
       "assets": [
         {
           "id": "env.arena",
           "type": "splat",
    -      "src": "@placeholder/arena.spz",
    +      "src": "levels/foundry.spz",
           "tags": ["world"],
    -      "collider": { "shape": "mesh", "src": "@placeholder/arena.collider.bin", "layer": "static" }
    +      "collider": { "shape": "mesh", "src": "levels/foundry.collider.bin", "layer": "static" }
         },

    @placeholder/... is a template convention: src/main.ts maps those onto the files inside gameable/placeholder. Anything else is resolved against baseUrl, so levels/foundry.spz (the file public/levels/foundry.spz) loads there.

    A world baked in examples/splat-bake has a .light.bin: add "bakedLighting": "levels/foundry.light.bin" to the entry. Then drop the arena's environmentLighting in src/main.ts, or it is lit twice.

    SPZ loads about four times faster than PLY; prefer it. The collider is a separate baked triangle mesh, because a splat has no geometry a solver can use — parseCollider in gameable/placeholder documents the format, and src/main.ts is where it is turned into a static body.

  2. Say which place the level plays in, and move the placements. place is the manifest id of the world; every position is in metres, Y-up, with yaw in degrees about +Y (0 faces +Z). A level actor's name is how the sandbox's edit mode finds it, so name what people will move.

    diff
    // src/level.json
     {
       "format": 1,
    +  "place": "env.arena",
       "actors": [
         {
           "name": "playerStart",
    -      "position": [0, 1.15, 9],
    +      "position": [-17, 1.15, 0],
    +      "yaw": 90,
           "class": "PlayerStart",
           "tags": ["arena"]
         },
         {
           "name": "guide",
    -      "position": [2.5, 1.05, 4],
    +      "position": [12, 1.05, -6],
           "yaw": -26.562,
           "class": "Npc",

    The level is loaded before init, so adding or removing an entry changes how many things exist. Nothing else needs to know. A game with several places keeps one level file per place: switch levels at runtime.

Verify ​

sh
npm test

The template's tests read placements from src/level.json through tests/level.ts, so a moved start moves the test with it. Then:

sh
npm run dev

Walk to each spawn point. You should land on the floor rather than falling through it or standing in the air: if you fall, the collider and the splat disagree about where the floor is, and the collider is what to fix.

See also ​