Supported logic-component fields become scene user_data and inspector properties automatically. metadata.properties is optional presentation and policy metadata; use it to rename, constrain, hide, order, or choose a widget for a field. Edit in the inspector in Edit mode; values seed the live actor on Play / scene load.
Deep reference: Scenes and gameplay. Sample: cube_entity.zig (health / max_health + transient current_health).
1. Declare fields + metadata
const hi = @import("hikari_game");
const BeaconLogic = hi.defineComponent(.{
.name = "beacon_logic",
.storage = .embedded,
.data = struct {
spin_speed: f32 = 1.0,
label_color: [3]f32 = .{ 1, 1, 1 },
/// Not serialized / not shown in the inspector.
runtime_angle: f32 = 0,
pub const metadata = .{
.properties = .{
.spin_speed = hi.UserDataPropertyOptions{ .label = "Spin Speed", .min = 0, .step = 0.05 },
.label_color = hi.UserDataPropertyOptions{ .label = "Color", .min = 0, .max = 1, .step = 0.01 },
.runtime_angle = hi.UserDataPropertyOptions{ .transient = true },
},
};
pub fn onChanged(self: *@This(), actor: hi.ActorContext, property: []const u8) void {
const id = actor.id;
_ = .{ self, id, property };
// React to inspector edits while live (Edit preview / Play).
}
pub fn update(self: *@This(), _: hi.ActorContext, ctx: *const hi.TickContext) void {
self.runtime_angle += self.spin_speed * ctx.dt;
}
},
});
pub const BeaconEntity = hi.defineActor(.{
.archetype = "beacon",
.components = .{BeaconLogic},
});| Option | Effect |
|---|---|
.label, .tooltip, .min, .max, .step, .order | Inspector presentation and ordering |
.hidden = true | Omitted from the authoring surface; typed gameplay code still owns the field |
.transient = true | Not authored in scene JSON; skipped by applyJson |
metadata = .{ .runtime_only = true } | No editor property schema; scene user_data may still seed JSON-safe fields |
runtime_only means “no inspector sheet”, not “cannot place in a scene” and not “scene cannot seed”. JSON-safe POD fields (bools, numbers, vectors, nested POD structs) still load from user_data; pointer/slice fields and anything marked transient are skipped. Reflection and metadata are compile-time/static declaration work: component instances gain no property bags, maps, allocations, or per-tick reflection.
2. Seed from scene JSON
{
"id": "beacon_01",
"archetype": "beacon",
"transform": { "position": [0, 1, 0], "rotation_euler": [0, 0, 0], "scale": [1, 1, 1] },
"user_data": {
"spin_speed": 2.5,
"label_color": [0.2, 0.8, 1.0]
}
}Omit keys to keep struct defaults. Multiple actors can share an archetype with different user_data (see demo disco_light_*).
3. Verify in the editor
- Rebuild, open the scene, select the actor.
- Inspector shows Spin Speed / Color; change a value —
onChangedruns when live. - Save the scene and confirm
user_datapersisted (non-transient fields only).
Next
- First motion — tween those fields
- First mesh and material