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
Tutorials3 min read

Tutorial: first messages

On this page
On this pageConcepts in one minuteZig → Zig / KawaKawa → doors / listenersOptional: read a field on another actorTry the messaging sceneGotchasNext Back to top

Prefer hi.actors / Actors.* for find, send, and (when needed) name-keyed component fields. Delivery is deferred until after entity updates in the same Play tick. Zig and Kawa both use the World event dispatcher; recipients run Zig handlers first, then Kawa handlers.

Deep reference: Actor communication. Sample: src/games/example/scenes/messaging.json and the message_* entities.

Concepts in one minute

CallMeaning
hi.actors.send / Actors.sendOne target (handle or scene id)
hi.actors.emit / Actors.emitEvery registered listener
Zig onMessageReceiver on logic components (runs before Kawa)
Kawa on_messageReceiver on each script slot that declares it
isAlive / is_aliveGeneration still live — refs never pin actors

Stable scene "id" strings are the usual address.


Zig → Zig / Kawa

Sender (sample pad — fires once on trigger enter). Prefer self-as-sender helpers so you don’t pass id as .from:

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

// ActorContext lifecycle (best):
// e.sendEventTo("msg_door_zig", "open", .{});
// e.sendEventTo("msg_door_kawa", "open", .{});
// e.emitEvent("ping", .{});

// ActorRef lifecycle (update(self, id, ctx)):
const a = hi.actors;
a.sendEventToFrom(id, "msg_door_zig", "open", .{});
a.sendEventToFrom(id, "msg_door_kawa", "open", .{});
a.emitEventFrom(id, "ping", .{});

Zig receiver (on a defineComponent data type):

zig
pub fn onMessage(_: *@This(), _: hi.ActorContext, msg: *const hi.Message) void {
    if (hi.actors.messageNameEql(msg.name, "open")) {
        // open door …
    }
}

Optional: branch on a live handle:

zig
if (hi.actors.tryFind("msg_door_zig")) |door| {
    a.sendEventFrom(id, door, "open", .{});
}
// Fire-and-forget does not need isAlive — dead targets no-op.

Kawa → doors / listeners

Kawa receiver (assets/scenes/messaging/scripts/message_door.kawa):

kawa
fn on_message(name, from, a0, a1, a2, a3) {
    if (name == "open") {
        // Actor.set_position(...)
    }
}

Send from script (preferred namespace):

kawa
Actors.send("msg_door_zig", "open");
Actors.emit("ping");

let door = Actors.find("msg_door_kawa");
if (Actors.is_alive(door)) {
    Actors.send(door, "open");
}

World.send / World.find still work; use Actors.* for new scripts.

Script-primary door archetype: message_kawa_door_entity.zig (default script asset://./scenes/messaging/scripts/message_door).


Optional: read a field on another actor

When you need data rather than an event. Prefer component-bound helpers so the component name is not repeated as a string (numbers / bools / enums here; string and asset field helpers are also available — see Actor communication):

zig
// Health = hi.defineComponent(.{ .name = "health", .data = struct { hp: f32 = 100 } });
if (hi.actors.tryFind("enemy_00")) |enemy| {
    if (enemy.component(Health).has()) {
        const hp = enemy.component(Health).get(.hp) orelse 0;
        _ = enemy.component(Health).set(.hp, hp - 10);
    }
}
kawa
if (Actors.has(enemy, "health")) {
    let hp = Actors.get_number(enemy, "health", "hp");
    Actors.set_number(enemy, "health", "hp", hp - 10);
}

Same-actor Zig siblings with known types: prefer ActorContext.get(Health) in lifecycle callbacks (see Actor communication — ActorContext).


Try the messaging scene

  1. Play physics_playground.json (or any scene with the sample session HUD).
  2. Choose Browse scenes → Tutorials → Messaging, or open scenes/messaging.json directly.
  3. Both doors start closed (low). The falling weight cube lands on the pad — Zig (blue) and Kawa (pink) doors lift; listeners receive ping. You can also walk onto the pad yourself after Reload.
  4. Reload / re-Play should restore both doors closed until the pad fires again.

Gotchas

  • Messages flush after updates — do not expect synchronous side effects in the same update. Game modules enqueue via hi.actors.send / sendToSceneId / emit only (no HostApi sendNow for games).
  • Kawa’s standalone Events.* bus is disabled in Hikari, including its events_* aliases. Use Actors.* for gameplay messages.
  • ActorRef is weak — despawn/unload invalidates handles; check isAlive. Refs never keep an actor loaded.
  • Additive layers require unique entity ids across all loaded layers.
  • Cross-actor “call a function by name” is not an API — send a message (or set a field).

Next

  • Actor communication — full API, fan-out, field access
  • Play, Edit, and scenes
  • First entity — add your own door / pad pair
  • Game-facing refs — ActorRef rules
PreviousTutorial: first input actionNext Tutorial: Play, Edit, and scenes

Documentation follows the current engine checkout.

Snapshot cc148c75Source docs/tutorials/first-messaging.md
On this pageConcepts in one minuteZig → Zig / KawaKawa → doors / listenersOptional: read a field on another actorTry the messaging sceneGotchasNext Back to top