# Tutorial: first entity

An entity is an **archetype** (string id) plus a tuple of **components**. Engine capabilities (`hi.ComponentRender`, `hi.ComponentPhysics`, …) and game logic (`defineComponent`) are listed together. The build recursively imports every `.zig` under the project `src/` into `content_manifest.gen.zig`; comptime registration picks out `defineActor` / `defineComponent` types — you do not maintain a hand registry, and files may live in any subdirectory.

Deep reference: [Scenes and gameplay](/docs/systems/scenes-and-gameplay.md), [Scripting with Kawa](/docs/guides/scripting-kawa.md). API reference: [Gameplay API](/docs/guides/gameplay-api.md).

## Setup checklist

1. Add a `.zig` file under the project `src/` (any folder; `entities/` is conventional, not required).
2. Export a `defineActor` value with a unique `.archetype` string (and any `defineComponent` types the archetype needs).
3. Rebuild the game/editor so `content_manifest.gen.zig` picks the file up.
4. Place an actor in scene JSON with the same `"archetype"` and a `"components"` map (scene document `"version": 1`).
5. Cameras look local **−Z**. Identity rotation at **+Z** looks at the origin; yaw 180 at +Z looks away. Copy `scenes/messaging.json` (`[0, 5, -14]`, yaw `180`) — see [coordinate space](/docs/systems/coordinate-space.md).
6. Press **Play** in the editor (or run the standalone game) to run component `update` / scripts.

If the archetype is missing from the manifest, scene load soft-fails to a `_missing` host instead of aborting.
Components listed by an exported entity may remain private declarations: `defineActor`
publishes their types to content discovery, which registers them before the
archetype. Export a component separately only when other source files need to
import it or when it is not referenced by an entity (for example, a purely
instance-addable component).

---

## Zig-only

Minimal marker (sample: empty-style archetypes):

```zig
const hi = @import("hikari_game");

pub const BeaconEntity = hi.defineActor(.{
    .archetype = "beacon",
});
```

With engine capabilities and game logic as components:

```zig
const hi = @import("hikari_game");

const BeaconLogic = hi.defineComponent(.{
    .name = "beacon_logic",
    .data = struct {
        speed: f32 = 1,
        state: struct { spin: f32 = 0 } = .{},

        pub fn update(self: *@This(), _: hi.ActorContext, tick: *const hi.TickContext) void {
            self.state.spin += self.speed * tick.dt;
        }
    },
});

pub const BeaconEntity = hi.defineActor(.{
    .archetype = "beacon",
    .components = .{ hi.ComponentRender, BeaconLogic },
});
```

Components default to `.attachable`: they work in archetypes and in the editor's
Add Component menu. Use explicit `.embedded` only for fixed composition; it has
the same authored fields but cannot be added to arbitrary actors. Populations
without actor identity use `defineSwarm`. See [Gameplay API](/docs/guides/gameplay-api.md).

Optional lifecycle methods on the **logic component** `data` struct: `awake`, `start`, `update`, `lateUpdate`, `fixedUpdate`, `onCollision`, `onMessage`, `onChanged`, `onDestroy`, `deinit`.

Cross-actor talk and name-keyed fields: **`hi.actors` / `Actors.*`**. Same-actor typed siblings in Zig: `ActorContext.get` / `require`. See [Actor communication](/docs/systems/entity-messaging.md) and [First messages](/docs/tutorials/first-messaging.md).

Built-in capability components (engine):

| Symbol | Capability |
|--------|------------|
| `hi.ComponentRender` | Mesh / material draw |
| `hi.ComponentPhysics` | Body / collider |
| `hi.ComponentCamera` | Camera |
| `hi.ComponentLight` | Light |
| `hi.ComponentAudio` | Audio source |
| `hi.ComponentVisualZone` | Visual zone |
| `hi.ComponentTransform` | Explicit transform (always present on live entities; rarely listed) |

Scene actor (`com.hikari.scene` document **version 1**). Capabilities live under `"components"`, not top-level `"render"` / `"physics"`:

```json
{
  "id": "beacon_01",
  "name": "Beacon",
  "archetype": "beacon",
  "transform": {
    "position": [0, 1, 0],
    "rotation_euler": [0, 0, 0],
    "scale": [1, 1, 1]
  },
  "components": {
    "render": {
      "mesh": "asset://./models/cube",
      "material": "asset://./materials/cube"
    }
  }
}
```

Scene document root uses numeric `"version": 1`, with `actors` at the top level. Shinra cooks `.shinscene` from the same form.

Sample Zig actors to copy: `floor_entity.zig`, `cube_entity.zig`, `disco_light_entity.zig`.

### Active / enable

Active / enable trio on the live actor (defaults all on):

| Switch | Scene | Runtime |
|---|---|---|
| Entity | `"active": false` | `hi.world().setActive(id, false)` — stops update/scripts/messages; gates physics + render |
| Physics | `"components": { "physics": { "is_active": false } }` | `hi.world().setPhysicsActive(id, false)` — body out of sim |
| Render | `"components": { "render": { "is_visible": false } }` | `hi.render().setVisible(id, false)` — skip draw |

Full rules: [Scenes and gameplay — Active / enable trio](/docs/systems/scenes-and-gameplay.md#active--enable-trio).

---

## Kawa-only (script-primary)

Declare capabilities and a default script; put game logic in the script only:

```zig
const hi = @import("hikari_game");

pub const BannerEntity = hi.defineActor(.{
    .archetype = "banner",
    .components = .{hi.ComponentRender},
    .script = "asset://./scripts/banner",
});
```

Script (`assets/scripts/banner.kawa` — extension-free stem in the ref):

```kawa
fn start() {
    Debug.log("banner ready: " + Actor.get_scene_id());
}

fn update(dt, total_time) {
    _ = dt;
    _ = total_time;
}
```

Entity script hooks: `start`, `update`, `late_update`, `on_collision`, `on_message`. Scripts do **not** run in Edit mode — enter Play.

Sample: `message_kawa_door_entity.zig` + `assets/scenes/messaging/scripts/message_door.kawa`.

A scene actor may also override the script without changing the archetype:

```json
{
  "script": { "path": "asset://./scripts/collision_logger" }
}
```

(`cube_00` in `scenes/physics_playground.json` does this.)

---

## Scripted component

A script can also live on a **component** instead of the archetype. The component keeps typed Zig fields (inspector-editable, serialized under `"components"`), and the script sees them as `data`:

```zig
const hi = @import("hikari_game");

pub const Patrol = hi.defineComponent(.{
    .name = "patrol",
    .script = "asset://./scripts/patrol",
    .data = struct {
        speed: f32 = 2.0,
        radius: f32 = 12.0,
    },
});

pub const GuardEntity = hi.defineActor(.{
    .archetype = "guard",
    .components = .{ hi.ComponentRender, Patrol },
});
```

One script slot attaches per component instance; an actor with two scripted components plus an entity `.script` has three slots. A scene actor overrides a component's script inside that component's block:

```json
{
  "components": {
    "patrol": { "speed": 3.0, "script": { "path": "asset://./scripts/patrol_aggressive" } }
  }
}
```

Engine capability builtins and `.swarm` components reject `.script` at comptime. Detail: [Scripting with Kawa](/docs/guides/scripting-kawa.md). Working sample: `hikari-games/example/scenes/script_interop.json` (reactor = Zig data + component script + entity script on one actor).

---

## Mixed Zig + Kawa

Engine builtins already provide bare `empty` / `light` / `camera`. Genre cameras layer logic + script on a **new** archetype name (sample `fly_camera`):

```zig
const hi = @import("hikari_game");

const FlyCameraLogic = hi.defineComponent(.{
    .name = "fly_camera_logic",
    .data = struct {
        move_speed: f32 = 14,
        // Add authored fields here. Revise the script to read the same fields.
    },
});

pub const FlyCameraEntity = hi.defineActor(.{
    .archetype = "fly_camera",
    .components = .{ hi.ComponentCamera, FlyCameraLogic },
    .script = "asset://./scripts/camera_controller",
});
```

Scene `user_data` / component properties seed Zig fields; Kawa can read the same data. Do not redefine host builtins (`empty` / `light` / `camera` / `visual_zone`).

| Mode | Logic component(s) | `.script` | Authored schema |
|---|---|---|---|
| Zig-only | yes | no | from supported data fields; optional metadata customizes them |
| Hybrid | yes | yes | from supported data fields |
| Script-primary | no | yes | none (or inspector-only) |
| Scripted component | yes (`defineComponent.script`) | per component | from that component's fields |

---

## Verify

1. Rebuild: `bin/kaji/kaji editor --workspace=$PWD --project=$PWD/../hikari-games/example --type=dynamic --config=debug --run` (or your usual product command).
2. Open the scene, confirm the actor appears in the hierarchy.
3. Enter Play — component `update` and Kawa hooks run; entity timing (Profiler → Entity timing) can confirm costs.

## Next

- Cross-scene HUD / state → [First session services](/docs/tutorials/first-session-services.md)
- Draw widgets from an entity → [First UI](/docs/tutorials/first-ui.md)
- Wire a key → [First input action](/docs/tutorials/first-input.md)
