Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Scenes and prefabs

A scene is a whole world kept in a file: a level, a menu, a test setup. A prefab is one entity’s components, kept in a file and spawned as many times as you like: a tree, an enemy, a building. Both live in assets/, both are JSON (so they diff well in git), and the editor, agents and your own code write them like any other file.

#![allow(unused)]
fn main() {
fn beach(ctx: &mut Ctx) -> Result<(), String> {
    ctx.world.reflect::<Palm>(); // components in files must be reflected
    ctx.load_scene("levels/beach.json")?; // the level: its entities, ids kept
    for i in 0..3 {
        // three more palms, all alike, in a row
        ctx.spawn_prefab("prefabs/palm.json", vec2(400.0 + i as f32 * 64.0, 400.0))?;
    }
    Ok(())
}
}
struct Palm { float height; };

static void beach(crabby::ctx& ctx) {
    ctx.world().reflect<Palm>({crabby::field("height", &Palm::height)});   // components in files must be reflected
    ctx.load_scene("levels/beach.json");   // the level: its entities, ids kept
    for (int i = 0; i < 3; ++i)            // three more palms, all alike, in a row
        ctx.spawn_prefab("prefabs/palm.json", {400.0f + float(i) * 64, 400});
}
  • load_scene replaces the world with the scene’s entities, which keep their ids, so components that refer to other entities still point at the right ones. Resources that are reflected load too.
  • spawn_prefab puts the prefab’s position at the point you give and keeps its saved rotation and scale. A prefab without a Transform2D gets one. The file is read once, then kept in memory, so spawning a thousand trees reads it once.
  • Only reflected components go in files: reflect your types before loading or saving. Transform2D, Sprite and the physics components are reflected already. A sprite names its image ("texture": "crab.png"), and loading the scene or prefab loads it. With 3D on, Transform3D and Model are reflected too: a model names its mesh’s glTF file ("mesh": "tree.glb"), loaded the same way. Scenes can also come from Unity (Importing from Unity).
  • Errors say what’s wrong and change nothing: a missing file lists the folders searched, and a component that isn’t reflected is named.

Saving

The inspector’s editor saves and loads scenes and places prefabs with the mouse. From code:

save_scene(path) writes the world (its reflected components and resources) and save_prefab(entity, path) writes one entity, without its position. Both write over the file in assets/ if it’s there, else create it (and its folders). Web builds can’t save files.

#![allow(unused)]
fn main() {
ctx.save_scene("levels/beach.json")?;
ctx.save_prefab(palm, "prefabs/palm.json")?;
}

C++: ctx.save_scene(path) and ctx.save_prefab(e, path), which throw on errors like the rest of the wrapper. C: crabby_ctx_load_scene, crabby_ctx_save_scene, crabby_ctx_spawn_prefab and crabby_ctx_save_prefab.

The files

A scene is the save format of world.save():

{
  "format": "crabby-world",
  "version": 1,
  "entities": [
    { "entity": 0, "components": { "Transform2D": { "position": [0.0, 400.0] }, "Palm": { "height": 3.0 } } },
    { "entity": 1, "components": { "Transform2D": { "position": [200.0, 380.0] }, "Palm": { "height": 5.0 } } }
  ]
}

A prefab lists one entity’s components:

{
  "format": "crabby-prefab",
  "version": 1,
  "components": {
    "Transform2D": { "rotation": 0.1 },
    "Palm": { "height": 4.0 }
  }
}

Fields left out take their defaults (here the palm’s position and scale). Prefabs are single entities for now: a prefab can’t hold children.