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
| Call | Meaning |
|---|---|
hi.actors.send / Actors.send | One target (handle or scene id) |
hi.actors.emit / Actors.emit | Every registered listener |
Zig onMessage | Receiver on logic components (runs before Kawa) |
Kawa on_message | Receiver on each script slot that declares it |
isAlive / is_alive | Generation 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:
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):
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:
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):
fn on_message(name, from, a0, a1, a2, a3) {
if (name == "open") {
// Actor.set_position(...)
}
}Send from script (preferred namespace):
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):
// 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);
}
}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
- Play
physics_playground.json(or any scene with the sample session HUD). - Choose Browse scenes → Tutorials → Messaging, or open
scenes/messaging.jsondirectly. - 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. - 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 viahi.actors.send/sendToSceneId/emitonly (no HostApisendNowfor games). - Kawa’s standalone
Events.*bus is disabled in Hikari, including itsevents_*aliases. UseActors.*for gameplay messages. ActorRefis weak — despawn/unload invalidates handles; checkisAlive. 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 —
ActorRefrules