Skip to content
hikari
RenderingEditorAIToolchainDocumentation
GitHub
hikari

© 2026 Flying Rat Studio.
All rights reserved.

Explore the engineDocumentationContributorsLicenseBack to top
Documentation / Tutorials
Browse docs
Overview
Tutorials16
OverviewFirst game projectFirst entityAuthored user_data and the inspectorFirst mesh and materialFirst physics body and triggerFirst character controllerFirst session servicesFirst UIFirst input actionFirst messagesPlay, Edit, and scenesFirst runtime spawnFirst motionFirst skeletal animationAssets in Play (soft refs and hot reload)Dynamic editor recompile
Guides16
OverviewDevelopment guideGameplay APIChoosing component storageBuild and packagingProject filePluginsHikari Plugin APIData-driven content, JSON, and pathsScripting with KawaShader authoringTime of dayUser interfaceUI layoutUI widgetsMigration from Unity / UnrealActor and component lifecycle
Systems28
OverviewArchitectureApplication lifecycleFrontends and driversPlatforms and supportSession services and cross-scene stateScenes and gameplayActor communication (hi.actors)Game-facing refsSave / replication wire versionCoordinate space and camera conventionsRenderingRenderer architecture mapFrame governorGPU particlesVisual ZonesVolumetric mediaInputAudioPhysicsMotion KitTemporal KitAssets and ShinraPrefabsAsset residencyAsset formats (Shinra pipeline)UI and editorEditor asset hot reloadEditor Project Selector
Language reference2
OverviewAkari language referenceKawa language reference
Engine overview
Start exploring
  • No matching sections. Try fewer words or another topic.
NavigateEnter Openesc Close
Overview
Tutorials16
OverviewFirst game projectFirst entityAuthored user_data and the inspectorFirst mesh and materialFirst physics body and triggerFirst character controllerFirst session servicesFirst UIFirst input actionFirst messagesPlay, Edit, and scenesFirst runtime spawnFirst motionFirst skeletal animationAssets in Play (soft refs and hot reload)Dynamic editor recompile
Guides16
OverviewDevelopment guideGameplay APIChoosing component storageBuild and packagingProject filePluginsHikari Plugin APIData-driven content, JSON, and pathsScripting with KawaShader authoringTime of dayUser interfaceUI layoutUI widgetsMigration from Unity / UnrealActor and component lifecycle
Systems28
OverviewArchitectureApplication lifecycleFrontends and driversPlatforms and supportSession services and cross-scene stateScenes and gameplayActor communication (hi.actors)Game-facing refsSave / replication wire versionCoordinate space and camera conventionsRenderingRenderer architecture mapFrame governorGPU particlesVisual ZonesVolumetric mediaInputAudioPhysicsMotion KitTemporal KitAssets and ShinraPrefabsAsset residencyAsset formats (Shinra pipeline)UI and editorEditor asset hot reloadEditor Project Selector
Language reference2
OverviewAkari language referenceKawa language reference
Engine overview
Tutorials5 min read

Tutorial: first entity

On this page
On this pageSetup checklistZig-onlyActive / enableKawa-only (script-primary)Scripted componentMixed Zig + KawaVerifyNext Back to top

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, Scripting with Kawa. Design of the open model: Entity-component model.

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.
  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.

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 and First messages.

Built-in capability components (engine):

SymbolCapability
hi.ComponentRenderMesh / material draw
hi.ComponentPhysicsBody / collider
hi.ComponentCameraCamera
hi.ComponentLightLight
hi.ComponentAudioAudio source
hi.ComponentVisualZoneVisual zone
hi.ComponentTransformExplicit 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 packs and 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):

SwitchSceneRuntime
Entity"active": falsehi.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.


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: Script logic authoring. Working sample: src/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,
        // user_data schema fields as needed…
    },
});

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).

ModeLogic component(s).scriptAuthored schema
Zig-onlyyesnofrom component metadata.properties
Hybridyesyesfrom component metadata
Script-primarynoyesnone (or inspector-only)
Scripted componentyes (defineComponent.script)per componentfrom that component's fields

Verify

  1. Rebuild: bin/kaji/kaji editor --workspace=$PWD --project=$PWD/src/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
  • Draw widgets from an entity → First UI
  • Wire a key → First input action
PreviousTutorial: first game projectNext Tutorial: authored user_data and the inspector

Documentation follows the current engine checkout.

Snapshot cc148c75Source docs/tutorials/first-entity.md
On this pageSetup checklistZig-onlyActive / enableKawa-only (script-primary)Scripted componentMixed Zig + KawaVerifyNext Back to top