# Named points

> A sword that hangs from the hand in every frame: each frame of the sheet says where the hand is, and pointOf gives that place in the world.

Example `named-points`, running at https://nacatamalon.com/en/examples/named-points. The whole catalogue: https://nacatamalon.com/llms/examples.md.

The hand is somewhere else in every frame. Each frame of the sheet says where, by name, and `pointOf(sprite, 'hand')` gives that place in the world for the frame showing now, with the flip, the anchor, the scale and the turn applied. There is no table of offsets to keep in step with the art.

Here they are given to `createSpriteAtlas({ points })`; an `.atlas` file gives them the same way. The yellow marks are the points.

Click the preview first so it hears the keyboard. ← → walk, Z strikes.

## Code

`index.ts`:

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

const game = createGame('#app', {
    width: WIDTH,
    height: HEIGHT,
    background: '#3b4a5a',
    scaling: 'contain'
});

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

`art/hero.ts`:

```ts
import { pixelsFromRows, sheetFromRows } from 'nacatamalon/pixels';

export const createHeroArt = () => {
    const palette = {
        s: '#f2c79a',
        b: '#3a6ea5',
        B: '#274b73',
        k: '#222034',
        g: '#c9ccd4',
        y: '#ffb627',
        h: '#6b3e26'
    };

    const head = ['.....hhhh...', '....hhhhhh..', '....hssss...', '....sksks...', '....ssss....'];

    const hero = sheetFromRows({
        stand:  [...head, '...bbbbbb...', '...bbbbbbs..', '...bbbbbb...', '...BBBBBB...', '...BB..BB...', '...kk..kk...', '............'],
        stepA:  [...head, '...bbbbbb...', '...bbbbbbs..', '...bbbbbb...', '...BBBBBB...', '..BB....BB..', '..kk....kk..', '............'],
        stepB:  [...head, '...bbbbbb...', '...bbbbbs...', '...bbbbbb...', '...BBBBBB...', '....BBBB....', '....kkkk....', '............'],
        raise:  ['.....hhhh.ss', '....hhhhhhs.', '....hsssss..', '....sksks...', '....ssss....', '...bbbbbb...', '...bbbbbb...', '...bbbbbb...', '...BBBBBB...', '...BB..BB...', '...kk..kk...', '............'],
        strike: [...head, '...bbbbbb...', '...bbbbbbsss', '...bbbbbb...', '...BBBBBB...', '...BB..BB...', '...kk..kk...', '............'],
    }, palette);

    const { frames } = hero;

    const POINTS = {
        [frames.stand]:  {
            hand: {
                x: 9.5,
                y: 6.5
            },
            feet: {
                x: 6,
                y: 11
            }
        },
        [frames.stepA]:  {
            hand: {
                x: 9.5,
                y: 6.5
            },
            feet: {
                x: 3,
                y: 11
            }
        },
        [frames.stepB]:  {
            hand: {
                x: 8.5,
                y: 6.5
            },
            feet: {
                x: 6,
                y: 11
            }
        },
        [frames.raise]:  {
            hand: {
                x: 11.5,
                y: 0.5
            },
            feet: {
                x: 6,
                y: 11
            }
        },
        [frames.strike]: {
            hand: {
                x: 11.5,
                y: 6.5
            },
            feet: {
                x: 6,
                y: 11
            }
        },
    };

    // How the sword is held in each frame: a turn, not a place.
    const HELD: Record<number, number> = {
        [frames.raise]: -1.2,
        [frames.strike]: 1.2
    };

    const swordArt = pixelsFromRows(['..g..', '..g..', '..g..', '..g..', '..g..', '.yyy.', '..h..', '.....'], palette);
    const mark = pixelsFromRows(['y'], palette);

    return {
        hero,
        frames,
        POINTS,
        HELD,
        swordArt,
        mark
    };
};
```

`components/character.ts`:

```ts
import { createSprite, createSpriteAtlas, createText, pointOf, useKeyboard, useSpriteAnimation, useUpdate } from 'nacatamalon';
import { createPixelTexture, outlinePixels } from 'nacatamalon/pixels';

import { createHeroArt } from '../art/hero';

export const Character = () => {
    const { hero, frames, POINTS, HELD, swordArt, mark } = createHeroArt();

    const body = createSprite({
        atlas: createSpriteAtlas({
            texture: createPixelTexture(hero.pixels),
            columns: hero.columns,
            points: POINTS
        }),
        anchor: {
            x: 0.5,
            y: 1
        },
        transform: {
            x: 60,
            y: 56
        },
    });

    const anim = useSpriteAnimation(body, {
        play: 'stand',
        clips: {
            stand: {
                frames: [frames.stand],
                fps: 1
            },
            walk: {
                frames: [frames.stepA, frames.stand, frames.stepB, frames.stand],
                fps: 8
            },
            strike: {
                frames: [frames.raise, frames.strike, frames.strike],
                fps: 10,
                loop: false,
                next: 'stand'
            },
        },
    });

    // Held near its hilt, so it turns about the hand.
    const sword = createSprite({
        texture: createPixelTexture(outlinePixels(swordArt, '#222034')),
        anchor: {
            x: 0.5,
            y: 0.8
        },
        zIndex: 1
    });

    const handMark = createSprite({
        texture: createPixelTexture(mark),
        zIndex: 2
    });

    const feetMark = createSprite({
        texture: createPixelTexture(mark),
        zIndex: 2
    });

    createText({
        text: 'Z STRIKE',
        transform: {
            x: 4,
            y: 4
        },
        tint: '#a0a8c0'
    });

    const keys = useKeyboard();
    let playing = 'stand';

    const play = (name: string) => {
        if (playing !== name) {
            playing = name;
            anim.play(name);
        }
    };

    useUpdate((delta) => {
        const walk = (keys.isDown('ArrowRight') ? 1 : 0) - (keys.isDown('ArrowLeft') ? 1 : 0);

        if (keys.justPressed('z')) {
            anim.play('strike');
            playing = 'strike';
        }

        // The run says what comes after a strike, so ask which one is going.
        if (playing === 'strike' && anim.clip !== 'strike') {
            playing = 'stand';
        }

        if (playing !== 'strike') {
            body.transform.x = Math.max(8, Math.min(112, body.transform.x + walk * 30 * delta));

            if (walk !== 0) {
                body.flipX = walk < 0;
            }

            play(walk !== 0 ? 'walk' : 'stand');
        }

        const hand = pointOf(body, 'hand');
        const feet = pointOf(body, 'feet');

        if (hand !== null) {
            sword.transform.x = hand.x;
            sword.transform.y = hand.y;
            handMark.transform.x = hand.x;
            handMark.transform.y = hand.y;
        }

        if (feet !== null) {
            feetMark.transform.x = feet.x;
            feetMark.transform.y = feet.y;
        }

        const turn = HELD[body.frame ?? 0] ?? 0.3;

        sword.transform.rotation = body.flipX ? -turn : turn;
    });
};
```

`scenes/demo.ts`:

```ts
import { createScene, useSpawn } from 'nacatamalon';

import { Character } from '../components/character';

export const Yard = () => {
    useSpawn(Character)();

    return createScene();
};
```

`settings.ts`:

```ts
export const WIDTH = 120;
export const HEIGHT = 68;
```
