Skip to content
KCKingdoms Connected Scripting APIScripting guides and API
GuideMaintainer authored

Camera and free camera (noclip)

Read where this client's camera is and what it points at, and take it over with NoClip to fly, spectate or frame a shot.

Camera reads the view this client is drawing through. NoClip takes that view over and flies it through the world, walls included: an admin fly mode, a spectator camera or a scripted shot.

Key.bind("f8", () => {
if (NoClip.isActive()) {
NoClip.disable();
} else {
NoClip.enable({ mode: "body", speed: 12 });
}
});

Camera.getPose() returns a CameraPose: the eye position, the unit vectors forward, right and up (roll included), the vertical fov in degrees and the aspectRatio.

It is whichever camera the game has now: behind the player, in a dialogue, in a cutscene or in a free flight. For a fixed view or a glide between points, use Camera.setShot. Use NoClip for free flight.

Camera.screenRay({ x, y, range }) returns a CameraRay: origin (the eye), a unit direction, and target (origin plus direction times range).

  • x and y run from -1 (left, bottom) to +1 (right, top). The default (0, 0) is the crosshair. From a web view, divide pixels by the screen size.
  • range is 100 metres by default and at most 4096.
const ray = Camera.screenRay({ range: 40 });
const hit = ray ? World.raycast(ray.origin, ray.target, { mode: "anything" }) : null;

Raycasts and nearby entities covers what World.raycast returns.

With the defaults, the player flies with the photo mode keys (forward, back, left, right, jump to rise, crouch to sink, fast movement to boost), holds Alt to crawl and turns with the mouse. The keys follow the Controls menu and the keyboard layout. It is the same camera the World Builder flies.

mode What happens to the body
body (default) travels with the camera through geometry, the world streams in around it, and it lands where the flight ends
camera stays where it stood; only the view moves

enable(options?) returns a string:

Result Meaning
enabled the flight started
alreadyActive one was already running; the new options are not applied
noCamera there is no level to put the camera in
cameraBusy World Builder (MapEditor) or a scripted camera shot holds the camera

F7 opens World Builder when this player has access; a client resource can also call MapEditor.open(). See Allow building in multiplayer. MapEditor.isOpen() says whether it is open now.

Option Meaning
mode body or camera, see above
input whether this machine’s keyboard and mouse fly it (default true)
speed metres a second, 0.05 to 400 (default 12)
fov degrees, up to 140; left out, the game’s own is kept

disable({ keepPosition }) ends the flight. By default the body stays where the camera stopped; keepPosition: false puts it back where it took off, facing the same way. In camera mode neither matters.

The game’s controls are held during the flight, so the character does not walk. Your key binds still fire, so a bind can turn the flight off.

A flight can end without your resource asking. noclipChanged reports every start and end:

Events.on("noclipChanged", (active, reason) => {
if (!active && reason !== "script") {
Hud.showInfoText(`Free camera ended (${reason}).`);
}
});
reason Cause
script a resource started or ended it
mapEditor the World Builder opened and took the camera
viewLost the level went away
sessionOver the session ended

While a flight runs you can place and aim the camera. Every setter returns false when no flight is running.

function frame(target: Vector3): void {
if (NoClip.enable({ mode: "camera", input: false }) !== "cameraBusy") {
NoClip.focus(target, 8); // face it, then stand 8 m back
}
}
Call Does
setPosition(position), getPosition() place or read the camera
setRotation({ yaw, pitch }), getRotation() degrees; yaw wraps to -180 to 180 (0 faces +Y, rising yaw turns left), pitch is clamped to -89 to 89
setPose(position, forward), lookAt(point), focus(target, distance?) place and aim in one call
getForward() the view direction
setSpeed(), getSpeed() flight speed, kept across flights
setFov(), getFov() field of view
getState() everything as one NoClipState, read on the same frame, or null

With input: false (or setInputEnabled(false) mid-flight), the keyboard stops flying the camera and you call move(input, deltaSeconds) each frame:

let last = Date.now();
const timer = setInterval(() => {
const now = Date.now();
const delta = (now - last) / 1000;
last = now;
NoClip.move({ strafe: 1, yaw: 20 * delta }, delta); // a slow orbit
}, 16);
// When the shot is over: clearInterval(timer); NoClip.disable();
  • forward, strafe and lift run from -1 to 1 along the heading, its right and world up. yaw and pitch are this frame’s turn in degrees.
  • boost multiplies the speed by six; crawl divides it by five.
  • A frame longer than 0.1 seconds counts as 0.1, so a stall does not launch the camera across the map.
  • move returns false while the built-in driver has the camera.

rayThroughScreen(x, y) gives the world direction through a point of the frame, and projectToScreen(point) does the reverse, in the same -1 to 1 coordinates as Camera.screenRay:

function pickUnderCentre(): WorldRayHit | null {
const from = NoClip.getPosition();
const direction = NoClip.rayThroughScreen(0, 0);
if (!from || !direction) {
return null;
}
const to = from.clone().add(direction.clone().mul(200));
return World.raycast(from, to, { mode: "anything" });
}

Camera.getPose() keeps working during a flight, since the free camera is the game’s active view while it runs.