Motion Kit (hi.motion, hi.Tween, hi.Spring, …) animates values you own — entity fields, subsystem HUD state, editor chrome. Do not hang timelines on UiContext.
Deep reference: Motion Kit. Sample: disco_light_data.zig (disco_light archetype).
Tween on an entity field
const hi = @import("hikari_game");
const FadeLight = struct {
intensity: f32 = 2.0,
fade: hi.Tween = .{},
pub const metadata = .{
.properties = .{
.intensity = hi.UserDataPropertyOptions{ .label = "Intensity", .min = 0 },
.fade = hi.UserDataPropertyOptions{ .transient = true },
},
};
pub fn start(self: *@This(), id: hi.ActorRef) void {
self.fade = hi.Tween.init(0.0, self.intensity, 1.5, .ease_out_cubic);
hi.render().setLight(id, .{ .intensity = 0.0 });
}
pub fn update(self: *@This(), id: hi.ActorRef, ctx: *const hi.TickContext) void {
hi.render().setLight(id, .{ .intensity = self.fade.update(ctx.dt) });
}
};Wave / helpers
const bob = hi.motion.wave(ctx.total_time * bob_speed + phase) * amplitude;
hi.world().setPosition(id, .{ x, base_y + bob, z });Respect reduce_motion when driving UI opacity/shift: use hi.motion.resolve (see Motion Kit + accessibility prefs).
HUD motion
Store Tween / Spring on GameSubsystem (or a scene HUD entity), sample into widget styles each onTick / update. Retained editor chrome uses opacity / shift on WidgetStyle plus overlay_motion.zig — same value layer, different host.
Verify
- Play a
disco_lightinphysics_playground.json— lights fade in and orbit. - Change intensity in the inspector — sample
onChangedrestarts a tween. - Enable Reduce motion in editor settings — UI transitions snap; gameplay waves still run unless you gate them yourself.