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 physics body and trigger

On this page
On this page1. Dynamic body (simple)2. Trigger volume3. Fire gameplay from a collisionVerifyNext Back to top

Opt into physics with hi.ComponentPhysics on the archetype, then author a physics block under scene "components". Scene-authored physics defaults to Static when body_type is omitted (or the whole block is missing on a physics-capable archetype) — set "body_type": "Dynamic" explicitly for props that should fall. Runtime spawn still defaults to Dynamic. Collisions reach Zig via onCollision and Kawa via on_collision.

Deep reference: Physics. Samples: trigger_entity.zig, cube_entity.zig, fall_sensor in scenes/physics_playground.json.

1. Dynamic body (simple)

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

pub const BoxEntity = hi.defineActor(.{
    .archetype = "box",
    .components = .{ hi.ComponentRender, hi.ComponentPhysics },
});
json
{
  "id": "box_01",
  "archetype": "box",
  "transform": { "position": [0, 2, 0], "rotation_euler": [0, 0, 0], "scale": [1, 1, 1] },
  "components": {
    "render": {
      "mesh": "asset://./models/cube",
      "material": "asset://./materials/cube"
    },
    "physics": {
      "body_type": "Dynamic",
      "collider_shape": "Box",
      "collider_box_size": [1, 1, 1],
      "mass": 1.0
    }
  }
}

Render scale and collider size are independent when hand-authored. To size the collider from the mesh local AABB (box / sphere / capsule, local space — transform scale is applied at body create):

zig
_ = hi.world().fitColliderToRender(id);
// or read bounds yourself:
if (hi.render().localBounds(id)) |aabb| {
    const min_v, const max_v = aabb;
    _ = .{ min_v, max_v };
}

Common body_type values in samples: Dynamic, Static, Trigger.

Optional "is_active": false parks the body without removing the component (entity "active": false also disables effective physics). "enabled" is accepted as an alias, matching how light / visual_zone spell the same switch; is_active stays canonical and is what gets written back on save. Runtime: hi.world().setPhysicsActive(id, false). See Active / enable trio.

2. Trigger volume

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

const TriggerLogic = hi.defineComponent(.{
    .name = "trigger_logic",
    .storage = .embedded,
    .data = struct {
        pub const metadata = .{ .runtime_only = true };

        pub fn onCollision(_: *@This(), _: hi.ActorContext, info: *const hi.CollisionInfo) void {
            if (!info.other.isValid()) return;
            const other_name = hi.world().name(info.other);
            switch (info.phase) {
                .enter => std.log.info("enter {s}", .{other_name}),
                .exit => std.log.info("exit {s}", .{other_name}),
                .stay => {},
            }
        }
    },
});

pub const TriggerEntity = hi.defineActor(.{
    .archetype = "trigger",
    .components = .{ hi.ComponentPhysics, TriggerLogic },
});
json
{
  "id": "fall_sensor",
  "archetype": "trigger",
  "transform": { "position": [0, 6, 0], "rotation_euler": [0, 0, 0], "scale": [1, 1, 1] },
  "components": {
    "physics": {
      "body_type": "Trigger",
      "collider_shape": "Box",
      "collider_box_size": [24, 0.5, 24]
    }
  }
}

Triggers do not need a mesh. Play, drop a dynamic cube through the volume, watch ENTER/EXIT logs.

3. Fire gameplay from a collision

Sample message_pad_entity.zig sends messages on first .enter — combine with First messages.

Verify

  1. Play scenes/physics_playground — cubes fall; FallSensor logs when they pass through.
  2. Edit mode — no simulation (bodies frozen).
  3. Stop — document transforms restore; play-time physics poses do not write back.

Next

  • Character controller → First character controller
PreviousTutorial: first mesh and materialNext Tutorial: first character controller

Documentation follows the current engine checkout.

Snapshot cc148c75Source docs/tutorials/first-physics.md
On this page1. Dynamic body (simple)2. Trigger volume3. Fire gameplay from a collisionVerifyNext Back to top