Skip to content

Controls

Controls give a feature its own named input actions instead of reading a hard coded game button. The compiler writes the declarations to inputs.json; the game creates InputAction objects and lets the player remap them in the module settings.

Controls exist only on the client.

src/client/controls.ts
import {
Gamepad,
Keyboard,
defineControls,
} from "@killscript/sdk/client";
export const controls = defineControls("camera", {
toggle: Keyboard.F6,
boost: Keyboard.LeftShift,
controllerToggle: Gamepad.North,
});

The namespace produces action names such as camera.toggle. It prevents a generic name from colliding with another declaration in the same module.

defineControls() must initialize a top-level const, but the declaration may live in any imported client file.

Use an event when something should happen once, and polling when behavior continues while the button is held:

controls.toggle.onPressed(() => {
cameraEnabled = !cameraEnabled;
});
const boostLoop = controls.boost.whilePressed(() => {
moveCamera(fastSpeed);
});
if (controls.boost.isPressed()) {
drawBoostIndicator();
}
Handle methodBehavior
onPressed(callback)Uses the native performed event; intended for a module-lifetime handler
onChanged(callback)Reports both press and release transitions
onReleased(callback)Reports only a pressed-to-released transition
whilePressed(callback)Runs every client frame while held
isPressed()Reads the current state safely
native()Returns the underlying InputAction, if available

The polling watchers return an EventSubscription. Cancel it directly or add it to a feature scope when the watcher belongs to a toggleable feature.

import { Keyboard, Mouse, control, defineControls } from "@killscript/sdk/client";
const actions = defineControls("movement", {
charge: control.hold(Keyboard.F, 0.4),
quickTap: control.tap(Keyboard.G, 0.2),
doubleTap: control.multiTap(Keyboard.W, 2, 0.3),
onRelease: control.onRelease(Keyboard.Space),
});

These helpers generate Unity Input System interaction strings. The duration is in seconds. Use control(...) with a binding object for processors, groups or a custom interaction, and control.raw(...) when you need to specify the action type as well:

const look = defineControls("camera", {
delta: control.raw({
type: "PassThrough",
binding: { path: Mouse.Delta },
}),
});

The input helpers accept generated control handles and native InputAction objects:

import { input } from "@killscript/sdk/client";
if (input.anyPressed([controls.toggle, controls.boost])) {
showInputIndicator();
}
const chord = input.onChord(
[controls.toggle, controls.boost],
resetCamera,
);
const enabledState = input.toggle(controls.toggle, false, (value) => {
value ? enableFeature() : disableFeature();
});
print(`Enabled: ${enabledState.value}`);
  • anyPressed() and allPressed() read the current combined state;
  • onAnyPressed() runs when the group changes from idle to at least one press;
  • onChord() runs when every supplied action becomes pressed;
  • toggle() keeps a boolean state and flips it on each performed event.

toggle() is runtime state. It does not add a persistent setting to config.json.

ToggleState memberBehavior
valueCurrent boolean state
set(value)Replaces the state and calls the callback
flip()Flips the state, calls the callback and returns the new value