# Tutorial: first input action

Gameplay code should read **named actions**, not raw key codes. Bindings live in JSON; Zig and Kawa both resolve the same ids.

Deep reference: [Input](/docs/systems/input.md).

## 1. Add an action

Edit `configs/input_actions.json` in your project (runtime path `config://input_actions.json` — not in startup config). Add an entry to the `"actions"` array:

```json
{
  "kind": "com.hikari.config.input_actions",
  "version": 2,
  "actions": [
    {
      "id": "interact",
      "bindings": {
        "keys": ["r"],
        "gamepad_buttons": ["x"]
      }
    }
  ]
}
```

Bindings use readable control names, and arrays allow more than one control from the
same device. Omit fields that do not apply; the defaults are scale `1`, deadzone
`0.15`, and player `0`. See the [input reference](/docs/systems/input.md#authored-format)
for every accepted name and scalar-axis examples.

Rebuild / relaunch so the action map reloads.

## 2. Read from Zig

In an entity `update`:

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

pub fn update(_: *@This(), _: hi.ActorContext, _: *const hi.TickContext) void {
    if (hi.world().actionPressed("interact")) {
        std.log.info("interact", .{});
    }
}
```

Held state is `hi.world().actionHeld("move_forward")`. `actionReleased` reports the
release edge, while `actionValue` returns a named scalar from digital pairs, gamepad
axes, mouse delta, or wheel input. Compose two named scalars with `actionVector2`:

```zig
const move = hi.world().actionVector2("move_x", "move_y");
```

Use `actionBinding` / `setActionBinding` for runtime rebinding. Changes affect the next
input resolve and differences from authored defaults persist in the user's sparse
`configs/input_bindings.json` when the session shuts down. Projects continue to own
the action list and defaults in `configs/input_actions.json`. The current desktop input
backend owns player slot `0`; other player slots return disconnected until
multi-controller backend support lands.

Samples: `player_entity.zig` (`actionHeld` for move); session HUD toggles profiler/visualizers in `session_ui.zig` (F3/F4/F5/F6).

## 3. Read from Kawa

```kawa
fn update(dt, total_time) {
    _ = dt;
    _ = total_time;
    if (Input.pressed("interact")) {
        Debug.log("interact");
    }
    if (Input.held("move_forward")) {
        // ...
    }
}
```

Sample: `assets/scripts/camera_controller.kawa` (`Input.held("move_forward")`, `Input.mouse_left` / `mouse_delta` + `Actor.set_rotation_euler` for view).

## Verify

1. Play the scene that contains your entity/script.
2. Press the bound control — log line or gameplay response once per press (`pressed`) vs while held (`held`).
3. Confirm Edit mode does not run entity updates (actions will not fire there).

## Next

- [First messages](/docs/tutorials/first-messaging.md)
- [First entity](/docs/tutorials/first-entity.md) — attach the action to a new actor
