Razorbill

Getting Started

Creating a Project

Use the CLI to initialize a new project:

./build/toolchain/cli/eng_cli init ~/MyProject

This creates the project structure:

MyProject/
├── Content/
│   ├── Scenes/       # Scene files (.scene)
│   ├── Scripts/       # C++ scripts (.cpp + .scriptasset)
│   ├── Prefabs/       # Reusable prefabs (.prefab)
│   └── Materials/     # Material assets
├── Library/           # Cooked cache (meshes, textures, compiled scripts)
└── Build/             # Player output

Creating a Scene

./build/toolchain/cli/eng_cli new-scene ~/MyProject Main

This creates Content/Scenes/Main.scene with a default scene structure.

Bundled Player Prefabs

New projects include two bundled prefabs in Content/Prefabs/:

  • Player.prefab — Basic player with CharacterController, capsule collider, and first-person camera
  • animatedPlayer.prefab — Animated player with SkinnedMeshComponent, AnimatorComponent with locomotion blend tree, and CharacterController

Instantiate either prefab to get a playable character immediately:

{
  "op": "InstantiatePrefab",
  "params": { "prefab_name": "Player" }
}

Or via AI: "Add a player to the scene" — the agent automatically routes to the appropriate prefab.

Adding Entities

Entities are created through typed operations. Using the CLI:

# Create a cube entity
./build/toolchain/cli/eng_cli apply-plan ~/MyProject plan.json --json

Where plan.json contains operations:

[
  {
    "op": "CreatePrimitive",
    "params": {
      "primitive_type": "Cube",
      "entity_name": "MyCube",
      "position": [0, 1, 0]
    }
  },
  {
    "op": "CreatePBRMaterial",
    "params": {
      "entity_name": "MyCube",
      "base_color": [0.2, 0.5, 1.0, 1.0],
      "metallic": 0.0,
      "roughness": 0.4
    }
  }
]

Or use the AI Agent to do it with natural language:

./build/toolchain/cli/eng_cli agent ~/MyProject Main \
  --prompt "Create a blue cube at position 0,1,0 with smooth PBR material"

Writing Scripts

Scripts are C++ files that get hot-reloaded at runtime. Create one:

./build/toolchain/cli/eng_cli apply-plan ~/MyProject - --json <<'EOF'
[{
  "op": "CreateScript",
  "params": {
    "script_name": "Spinner",
    "template_type": "empty"
  }
}]
EOF

Then edit Content/Scripts/Spinner.cpp:

#include <eng/sdk/script_context.hpp>
#include <cmath>

extern "C" {

void OnStart(ScriptContext* ctx) {
    ctx->log_message(ctx, "Spinner started!");
}

void OnUpdate(ScriptContext* ctx) {
    float dt = ctx->get_delta_time();
    float rot[4];
    ctx->get_rotation(ctx->self, rot);

    // Rotate around Y axis
    float angle = dt * 1.0f;
    float half = angle * 0.5f;
    float sin_half = sinf(half);
    float cos_half = cosf(half);

    // Multiply quaternions (current * delta)
    float new_rot[4] = {
        rot[0] * cos_half + rot[2] * sin_half,
        rot[1] * cos_half + rot[3] * sin_half,
        rot[2] * cos_half - rot[0] * sin_half,
        rot[3] * cos_half - rot[1] * sin_half
    };
    ctx->set_rotation(ctx->self, new_rot);
}

void OnDestroy(ScriptContext* ctx) {}

}

Compiling and Attaching Scripts

# Compile all scripts
./build/toolchain/cli/eng_cli compile-scripts ~/MyProject

# Attach to an entity
./build/toolchain/cli/eng_cli attach-script ~/MyProject Main MyCube Spinner

Play Mode

Test your scene in play mode:

# Run headless for 60 frames
./build/toolchain/cli/eng_cli play ~/MyProject Main -f 60 --verbose

# Or launch the editor with auto-play
ENG_AUTO_PLAY=1 ./build/editor/eng_editor

Script Lifecycle

Every script can implement three functions:

FunctionCalledUse For
OnStartOnce when play mode beginsInitialization, finding entities, setting up state
OnUpdateEvery frameGame logic, movement, input handling
OnDestroyWhen play mode ends or entity is destroyedCleanup

Next Steps

On this page