# Camera with useLateUpdate

> A camera that follows the player from where it belongs, after every update of the scene, so the two never shake against each other.

Example `late-update-camera`, running at https://nacatamalon.com/en/examples/late-update-camera. The whole catalogue: https://nacatamalon.com/llms/examples.md.

`useLateUpdate` runs after every `useUpdate` of the scene, tweens and physics included, so a camera there reads where the player ended up this frame.

Press T to move the camera into `useUpdate` instead. The scene runs before the objects inside it, so the camera reads the player one step late: watch the white marker at the centre of the screen and the off-centre reading jump every time the player starts or stops.

## Code

`index.ts`:

```ts
import { createGame } from 'nacatamalon';
import { WIDTH, HEIGHT } from './settings';
import { Level } from './scenes/demo';

const game = createGame('#app', {
    width: WIDTH,
    height: HEIGHT,
    background: '#0d1020'
});

// Declare Scenes
game({
    Level
});
```

`components/hud.ts`:

```ts
import { createSprite, createText, useScreenSpace } from 'nacatamalon';
import * as Settings from '../settings';

export const Hud = () => {
    useScreenSpace();
    // The centre of the screen: the player should sit right on it.
    createSprite({
        width: 1,
        height: 30,
        tint: '#ffffff',
        zIndex: 5,
        transform: {
            x: Settings.WIDTH / 2,
            y: Settings.GROUND - 8
        }
    });

    const title = createText({
        text: '',
        transform: {
            x: 8,
            y: 8
        }
    });

    const gap = createText({
        text: '',
        transform: {
            x: 8,
            y: 20
        },
        tint: '#9aa4c0'
    });

    createText({
        text: 'T: SWAP   ← →: RUN',
        transform: {
            x: 8,
            y: Settings.HEIGHT - 14
        },
        tint: '#6b7390'
    });

    return {
        title,
        gap
    };
};
```

`components/player.ts`:

```ts
import { createSprite, useKeyboard, useTransform, useUpdate } from 'nacatamalon';
import * as Settings from '../settings';

export const Player = () => {
    const place = useTransform({
        x: 160,
        y: Settings.GROUND - 8
    });

    createSprite({
        width: 12,
        height: 16,
        tint: '#ffcc33',
        transform: {
            x: 0,
            y: 0
        }
    });

    const keys = useKeyboard();
    let speed = 0;

    useUpdate((delta, time) => {
        const asked = keys.isDown('ArrowRight') ? 1 : keys.isDown('ArrowLeft') ? -1 : 0;
        // No keys: run in bursts, so the speed keeps changing, which is when the lag shows.
        const target = asked !== 0 ? asked * 220 : (Math.sin(time * 2.2) > 0 ? 260 : 0);

        speed += (target - speed) * Math.min(1, delta * 6);
        place.x += speed * delta;
    });

    return place;
};
```

`components/terrain.ts`:

```ts
import { createSprite, getColor } from 'nacatamalon';
import * as Settings from '../settings';

export const Terrain = () => {
    // A long floor with posts, so the movement can be seen against something.
    for (let x = -2000; x < 4000; x += 16) {
        createSprite({
            width: 16,
            height: 40,
            tint: getColor(x % 32 === 0 ? '#323b60' : '#232a44'),
            anchor: {
                x: 0,
                y: 0
            },
            transform: {
                x,
                y: Settings.GROUND
            }
        });

        if (x % 64 === 0) {
            createSprite({
                width: 4,
                height: 34,
                tint: '#6f7cb0',
                anchor: {
                    x: 0.5,
                    y: 1
                },
                transform: {
                    x,
                    y: Settings.GROUND
                }
            });
        }
    }
};
```

`scenes/demo.ts`:

```ts
import { createScene, useCamera2d, useKeyboard, useLateUpdate, useSpawn, useUpdate } from 'nacatamalon';
import * as Settings from '../settings';
import { Player } from '../components/player';
import { Hud } from '../components/hud';
import { Terrain } from '../components/terrain';

export const Level = () => {
    const camera = useCamera2d();
    const keys = useKeyboard();

    useSpawn(Terrain)();

    const player = useSpawn(Player)();
    const hud = useSpawn(Hud)().api;

    let late = true;

    const follow = () => {
        camera.transform.x = player.api.x - Settings.WIDTH / 2;
    };

    const report = () => {
        // How far from the centre the player is drawn: what the eye sees as shaking.
        const drawnAt = player.api.x - camera.transform.x;

        hud.gap.text = `OFF CENTRE: ${(drawnAt - Settings.WIDTH / 2).toFixed(1)} PX`;
    };

    useUpdate(() => {
        if (keys.justPressed('t')) {
            late = !late;
        }

        hud.title.text = late ? 'CAMERA IN useLateUpdate' : 'CAMERA IN useUpdate (1 FRAME LATE)';

        if (!late) {
            follow();
        }
    });
    useLateUpdate(() => {
        if (late) {
            follow();
        }

        report();
    });

    return createScene();
};
```

`settings.ts`:

```ts
export const WIDTH = 320;
export const HEIGHT = 180;
export const GROUND = 140;
```
