Camera
Back to the reference

Camera

The 3D camera and the 2D one, picking through them, and aiming one.

15 symbols

cameraLookAtfunctionnacatamalon

cameraLookAt(camera: TCamera3d, target: { x, y, z }) => TCamera3d

Turns a camera to look at a point, from wherever it is standing.

It writes the camera's rotationY (left and right) and rotationX (up and down), and leaves its roll alone. Call it in useUpdate to keep a moving thing in the middle of the view. A camera turned by a quaternion is switched back to angles, since a quaternion would be read instead of them and the camera would not move.

Straight up or straight down has no left or right to speak of, so there the camera keeps the rotationY it had.

Parameters

cameraTCamera3d

The camera to turn.

target{ x, y, z }

Where to look: anything with x, y and z, such as another object's placement.

Example

declare const player: TTransform3d;

const camera = useCamera3d({ projection: 'perspective', fov: 60, y: 3, z: 6 });
useUpdate(() => cameraLookAt(camera, player));
View source · camera/camera_look_at.ts:30

screenToRayfunctionnacatamalon

screenToRay(camera: TCamera3d | null, width: number, height: number, x: number, y: number) => TRay

The ray that goes into the world through a point of the screen: what is under the pointer.

It is worked out from the very view and projection the scene is drawn with, so the ray passes through exactly what is drawn at that pixel. Test it against what can be picked (a sphere around each object, a box, a plane) and the nearest hit is what was clicked; against the floor's plane, it gives the point of the floor under the pointer, which is what dragging something across it needs.

x and y are in the game's pixels, the pointer's screenX and screenY, and width and height are the game's size (useGame().getWidth(), getHeight()). With no camera it uses the view the scene falls back to when it has none.

Parameters

cameraTCamera3d | null

The scene's camera, or null.

widthnumber

The game's width, in pixels.

heightnumber

The game's height, in pixels.

xnumber

The point, from the left.

ynumber

The point, from the top.

Example

const camera = useCamera3d({ projection: 'perspective', fov: 60, y: 3, z: 6 });
const game = useGame();
declare const marker: TTransform3d;

const pointer = usePointer();
pointer.onDown((info) => {
    const ray = screenToRay(camera, game.getWidth(), game.getHeight(), info.screenX, info.screenY);
    // How far along the ray the floor (y = 0) is, and the point there.
    const t = -ray.origin.y / ray.direction.y;
    marker.x = ray.origin.x + ray.direction.x * t;
    marker.z = ray.origin.z + ray.direction.z * t;
});
View source · camera/screen_to_ray.ts:73

useCamera2dfunctionhooknacatamalon

useCamera2d(options?: TCamera2dOptions) => TCamera2d

Gives the scene a camera, so its world can be bigger than the screen and scroll, turn and zoom.

Without one, everything is drawn where its x and y say on the screen, which is what a menu or a single-screen game wants. With one, those same numbers become a place in a world, and the camera decides which part of that world is on screen.

You get the camera back and move it by changing its numbers, usually every frame:

  • transform.x/transform.y: the world point on the top-left corner of the screen. To keep something in the middle, subtract half the screen from its position.
  • transform.rotation: turns the view, in radians.
  • zoom: 2 shows everything twice as big, 0.5 half as big.

The camera belongs to the whole scene, so it does not matter which part of the scene asks for it. Asking twice replaces the first one. Inside a picture made by createSpriteTexture it belongs to that picture instead, and the scene keeps its own.

Something that must stay still on screen while the world moves, a score or a health bar, has two ways to do it: put it in its own scene started with useScene().launch(), or mark it with useScreenSpace().

Parameters

optionsoptionalTCamera2dOptions

Where the camera starts: x, y, rotation and zoom, all optional.

Example

export const Level: TSceneFn = () => {
    const camera = useCamera2d();
    const hero = createSprite({ key: 'hero', transform: { x: 900, y: 600 } });

    useUpdate(() => {
        // The screen is 480x320: half of it keeps the hero in the middle
        camera.transform.x = hero.transform.x - 240;
        camera.transform.y = hero.transform.y - 160;
    });

    return createScene();
};
View source · hooks/camera/use_camera_2d.ts:51

useCamera3dfunctionhooknacatamalon

useCamera3d(options: TCamera3dOptions) => TCamera3d

Gives the scene a camera to look at its models through, and returns it.

Without one, models are drawn straight in the game's pixels, flat on, exactly the way sprites are drawn without a 2D camera. With one, the scene is seen from wherever the camera is.

There are two ways of seeing and the choice is what the game looks like. 'orthographic', the default, has no vanishing point and measures in the same pixels a sprite does: a model one unit wide is one pixel wide, so a 3D piece can stand on a 2D board and pan and zoom with it. 'perspective' is the usual one, where things shrink with distance.

What comes back is the camera itself, so moving it is changing a number and it takes effect on the next frame drawn. A scene has one, as it has one 2D camera; asking again gives back the one it already has, and says so, rather than quietly replacing it. Inside a picture made by createSpriteTexture it belongs to that picture instead, and the scene keeps its own.

Parameters

optionsTCamera3dOptions

Where it starts, how it sees, and how far it can see.

Example

const Level = () => {
    const camera = useCamera3d({ projection: 'perspective', z: 6 });

    useUpdate((delta) => {
        camera.transform.rotationY += delta * 0.5;
    });

    return createScene();
};
View source · hooks/camera/use_camera_3d.ts:42

useFogfunctionhooknacatamalon

useFog(options: TFogOptions) => TFog

Puts the scene in fog: its models fade into color between near and far from the camera.

The fog belongs to the scene, like its camera, so one is enough and a second is ignored with a warning. It covers every model the scene draws, a material of your own included, and leaves the 2D alone. Set the game's background to the same colour and the far end of the world melts into it instead of stopping at a wall, which is the whole trick.

Parameters

optionsTFogOptions

What it fades into and over what distance.

Example

export const Level: TSceneFn = () => {
    useCamera3d({ projection: 'perspective', z: 8 });
    const fog = useFog({ color: getColor('#1a1426'), near: 6, far: 40 });

    useUpdate((delta, time) => {
        // Breathing: the fog comes in and goes out
        fog.far = 40 + Math.sin(time) * 10;
    });

    return createScene();
};
View source · hooks/fog/use_fog.ts:36

useScreenSpacefunctionhooknacatamalon

useScreenSpace(on: boolean) => void

Keeps what is created here fixed on the screen, even while the scene's camera moves.

It is how a health bar, a score or a pause button stays in its corner over a world that scrolls. It covers everything this part of the scene creates and everything created under it, however deep, so a whole HUD is marked once.

Positions inside it are screen pixels, measured from the top-left corner, whatever the camera is doing. Called in the body of a scene, the whole scene ignores its camera.

The other way to get the same thing is a separate scene for the HUD, started with useScene().launch(): a scene without a camera is always fixed to the screen, and it is drawn over the scene that launched it.

Parameters

onboolean

false undoes it. Default true.

Example

const Hud = () => {
    useScreenSpace();
    createSprite({ tint: getColor('#4ade80'), width: 120, height: 8, transform: { x: 70, y: 16 } });
};

export const Level: TSceneFn = () => {
    useCamera2d();
    useSpawn(Hud)();
    return createScene();
};
View source · hooks/camera/use_screen_space.ts:37

worldToScreenfunctionnacatamalon

worldToScreen(camera: TCamera3d | null, width: number, height: number, point: { x, y, z }) => TScreenPoint

Where a point of the world is drawn on the screen: the other way round from screenToRay.

What puts a name over a character's head, a marker on something off in the distance, or a health bar that follows a model: a sprite placed at the answer sits exactly over the point, because the answer comes from the same view and projection the scene is drawn with.

Parameters

cameraTCamera3d | null

The scene's camera, or null.

widthnumber

The game's width, in pixels.

heightnumber

The game's height, in pixels.

point{ x, y, z }

The point in the world.

Example

const camera = useCamera3d({ projection: 'perspective', fov: 60, y: 3, z: 6 });
const game = useGame();
declare const enemy: TMesh;
declare const label: TText;

useUpdate(() => {
    const at = worldToScreen(camera, game.getWidth(), game.getHeight(), enemy.transform);
    label.visible = !at.behind;
    label.transform.x = at.x;
    label.transform.y = at.y - 20;
});
View source · camera/world_to_screen.ts:55

TCamera2dtypenacatamalon

The camera a scene looks at its 2D world through. Plain data: the renderer reads it every frame, so changing a field is all it takes to move, turn or zoom the view.

transform.x/y is the world point that lands on the top-left corner of the screen, in pixels, not its centre: following something means subtracting half the screen. Rotation and zoom pivot on that same corner.

zoom magnifies: 2 shows everything twice as big, 0.5 half as big.

type exists for the compiler as much as for anyone reading the data: without it, any record with a transform and a zoom would fit here by shape.

Properties

type'camera2d'
idstring
transform{ x, y, rotation }

Where the camera is and how it is turned. No scale: a camera does not stretch, and a field nothing reads is a field someone will set and wonder why nothing happened.

zoomnumber

Magnification. 1 is none, 2 twice as big.

View source · camera/types/t_camera_2d.ts:18

TCamera2dOptionstypenacatamalon

What a camera can start with. Everything is optional.

Properties

xoptionalnumber

The world point on the top-left corner of the screen, in pixels. Default 0.

yoptionalnumber
rotationoptionalnumber

In radians. Default 0.

zoomoptionalnumber

Magnification. Default 1.

View source · camera/types/t_camera_2d.ts:39

TCamera3dtypenacatamalon

The camera a scene looks at its 3D world through. Plain data, like the 2D one: the renderer reads it every frame, so changing a field is all it takes to move, turn or zoom the view.

Two ways of seeing, and the choice is what the game is:

  • 'perspective' is the usual one: things get smaller with distance, and fov says how much is in view.
  • 'orthographic' has no vanishing point and measures in the same pixels a sprite does: a model one unit wide is one pixel wide. That is what lets a 3D piece stand on a 2D board and pan and zoom with it, which is how the consoles of the era mixed the two.

Y points up here, the opposite of the 2D camera, because every model is built that way and mirroring the axis would turn each one inside out.

Properties

type'camera3d'
idstring
projection'perspective' | 'orthographic'
transformTTransform3d

Where the camera is and how it is turned. No scale: a camera does not stretch.

fovnumber

How wide the view is, in degrees. Perspective only.

nearnumber

Nothing closer than this is drawn, and nothing further than far.

farnumber
zoomnumber

Magnification. Orthographic only: 2 shows everything twice as big.

View source · camera/types/t_camera_3d.ts:22

TCamera3dOptionstypenacatamalon

What a 3D camera can start with. Everything is optional.

Properties

projectionoptional'perspective' | 'orthographic'

Default 'orthographic', which measures in pixels and lines up with the 2D.

xoptionalnumber

Where it is. Default the origin, looking along -Z.

yoptionalnumber
zoptionalnumber
rotationXoptionalnumber

Looking up and down, and turning left and right, in radians.

rotationYoptionalnumber
rotationoptionalnumber

Rolling, in radians.

fovoptionalnumber

In degrees. Default 60. Perspective only.

nearoptionalnumber

Default 0.1 and 1000.

faroptionalnumber
zoomoptionalnumber

Default 1. Orthographic only.

View source · camera/types/t_camera_3d.ts:52

TFogtypenacatamalon

The fog a scene is seen through: things fade into color as they get further from the camera.

It is what the Nintendo 64 is remembered by, and the way every game of the era hid how near it stopped drawing. Worked out per corner and smeared across each triangle, like the light, which is how that hardware did it too. Everything here is read every frame, so changing a field fades the fog in, thickens it or turns it to dusk with nothing set up again.

Properties

type'fog'
idstring
colorTColor

What things fade into. Usually the background, or the fog shows as a wall.

nearnumber

How far from the camera the fog begins. Nearer than this is clear.

farnumber

How far from the camera it is complete. Further than this is all fog.

enabledboolean

Whether it is drawn. Switching it off keeps the rest, so it can be switched back on.

View source · fog/types/t_fog.ts:15

TFogOptionstypenacatamalon

What useFog is asked for. Everything has a default, so useFog({ color }) is a fog.

Properties

coloroptionalTColor

What things fade into. Default black.

nearoptionalnumber

How far from the camera the fog begins. Default 10.

faroptionalnumber

How far from the camera it is complete. Default 100.

enabledoptionalboolean

Whether it is drawn. Default true.

View source · fog/types/t_fog.ts:43

TScreenPointtypenacatamalon

Where a point of the world lands on the screen.

x and y are in the game's pixels, from the top-left corner, the same ones a sprite is placed in. depth goes from 0 at the camera's near plane to 1 at its far one. behind says the point is behind the camera, where x and y mean nothing and whatever was going to be drawn there should not be.

Properties

xnumber
ynumber
depthnumber
behindboolean
View source · camera/world_to_screen.ts:16