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 session services

On this page
On this pageWhat people mean by “global”Minimal GameSubsystemSession Kawa scriptLoose runtime spawn (optional)VerifyNext Back to top

The engine has no “global entity” archetype. Cross-scene state and HUD belong on the optional GameSubsystem, which outlives scene replace. Scene JSON is for placeable content only.

Deep reference: Session services.

What people mean by “global”

IntentCorrect seam
HUD / progress / settings that survive Scene.loadGameSubsystem (+ optional session Kawa script)
Runtime actor not bound to an additive layerw.spawn(&.{ .layer = .loose, … }) — shows under Play hierarchy Global group
Fields not editable in the inspectorbehaviour metadata = .{ .runtime_only = true } (still placeable in scenes)

Do not duplicate a “GameManager” actor into every scene JSON.


Minimal GameSubsystem

Export from the game root (sample already does):

zig
// src/games/example/src/root.zig
pub const config = @import("config.zig").config;
pub const content_manifest = @import("content_manifest.gen.zig");
pub const GameSubsystem = @import("session.zig").GameSubsystem;

Skeleton (hooks are optional beyond create / destroy; call sites use hi.world() / hi.ui() — do not store a World pointer):

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

pub const GameSubsystem = struct {
    allocator: std.mem.Allocator,
    score: i32 = 0,

    pub fn create(allocator: std.mem.Allocator) !*GameSubsystem {
        const self = try allocator.create(GameSubsystem);
        self.* = .{ .allocator = allocator };
        return self;
    }

    pub fn destroy(self: *GameSubsystem) void {
        self.allocator.destroy(self);
    }

    pub fn onLaunch(self: *GameSubsystem) void {
        _ = self;
        // Register one-time session providers/resources.
    }

    /// Runs once per World — the initial one, and every disposable Play copy.
    /// One subsystem instance serves them all, so key per-World state by
    /// `scope.id` instead of assuming there is only ever one.
    pub fn onWorldAttach(self: *GameSubsystem, scope: hi.WorldScope) void {
        _ = self;
        _ = scope;
        // Reserve UI capacity and install world-local script natives.
    }

    pub fn onTick(self: *GameSubsystem) void {
        _ = self;
        // Draw cross-scene HUD; tick session Kawa if attached.
        // hi.world().updateSessionScript();
    }

    pub fn onSceneLoaded(self: *GameSubsystem, info: hi.SceneInfo) void {
        _ = self;
        _ = info;
    }

    pub fn onSceneWillUnload(_: *GameSubsystem) void {}
    pub fn onTerminate(_: *GameSubsystem) void {}
};

Useful hooks: onLaunch / onTerminate for the session, onWorldAttach / onWorldDetach for each initial or disposable Play World, onTick, onPlayEnded (editor Stop — clear play-ephemeral HUD here, not in unload), onSceneWillUnload / onSceneLoaded, onSceneLayerLoaded / onSceneLayerReady (a manual layer is loaded but not yet shown) / onSceneLayerWillUnload, onSceneReadyStage (none → entities → assets → gpu → world all).

onSceneLoaded means actors spawned only. For a loading screen, wait until hi.world().sceneLoadSnapshot().ready_stage == .all (or handle onSceneReadyStage), then fade out. Stage table: Session services — readiness.

Editor Stop is immediate because the authoring world is retained while a disposable Play world runs. Stop switches back before returning, then retires Play invisibly in bounded slices. Keep expensive shared resources on the subsystem — session-owned resources. Play/Edit rules: Play, Edit, and scenes.

Sample implementation: src/games/example/src/session.zig + session_ui.zig (session card, settings, Next Scene / Reload).


Session Kawa script

Attach once the asset store is ready (sample retries from onTick until bytecode is live):

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

const w = hi.world();
const script = hi.AssetRef.must(.script, "asset://./scripts/session");
w.attachSessionScript(script) catch |err| {
    std.log.info("session script: {any}", .{err});
};

Session scripts have no Actor.* — they are not entity scripts. Tick them from onTick via hi.world().updateSessionScript() (no-op outside a play tick; see sample).


Loose runtime spawn (optional)

When you need a live actor that is not authored in the current scene and should not die with an additive layer unload, call hi.world().spawn with .layer = .loose — see First runtime spawn.

Replace scene unload still tears down live world entities; only GameSubsystem state survives. For HUD that must persist across replace, draw from onTick, not from a scene actor.


Verify

  1. Rebuild and Play.
  2. Use the sample HUD “Next Scene” (or your own hi.world().requestSceneLoad) and confirm subsystem fields persist.
  3. Stop in the editor — the document scene returns; session services tear down only on session shutdown.

Next

  • First UI — draw the HUD
  • Play, Edit, and scenes — load rules and Stop semantics
PreviousTutorial: first character controllerNext Tutorial: first UI

Documentation follows the current engine checkout.

Snapshot cc148c75Source docs/tutorials/first-session-services.md
On this pageWhat people mean by “global”Minimal GameSubsystemSession Kawa scriptLoose runtime spawn (optional)VerifyNext Back to top