Hitboxes
Back to the reference

Hitboxes

The parts of an object that can be touched, and what to do when two of them meet: hits, pickups and triggers without a physics engine.

12 symbols

collisionBetweenfunctionnacatamalon

collisionBetween(a: TCollisionTarget, b: TCollisionTarget) => boolean

Whether two things touch right now: a bullet and a brick, the player and a door, a sword and whatever is in front of it. With no physics: just whether their rectangles share any area.

Each side can be an object, a sprite, a text, a panel, or a rectangle of the world ({ x, y, width, height }, from its top-left corner). An object is measured by its hitboxes (useHitbox) when it has any, and by what it draws when it has none. Everything is measured where it is drawn: turned, grown and moved with everything above it. Edges that only meet do not count, so two tiles side by side are not touching.

For many things against many, every frame, useCollisionBetween with components or tags says it once.

Parameters

aTCollisionTarget

One thing.

bTCollisionTarget

The other.

Example

declare const bullet: TSprite;
declare const brick: TSprite;

useUpdate(() => {
    if (collisionBetween(bullet, brick)) {
        bullet.visible = false;
    }
});
View source · hitbox/collision_between.ts:80

useCollisionBetweenfunctionhooknacatamalon

useCollisionBetween(a: A, b: B, callback: (a: TCollisionMatched<A>, b: TCollisionMatched<B>) => void) => () => void

Calls callback for every thing of kind a touching one of kind b, every frame they touch: a rule of the scene, shots against enemies, the player against coins. A piece says itself what it collides with with useCollisionWith; this is for the scene that knows both. Each kind is a component, whose pieces come with their api typed, or a tag, as in useCollisionWith, and physics bodies meet as they do there.

Run once every useUpdate of the scene is over, before useLateUpdate, measured as collisionBetween measures. The two come in the order the tags were named. Once one is destroyed it is not called with again, so a bullet hits one enemy and not the three behind it. A tag can be named twice, useCollisionBetween('enemy', 'enemy', ...), and each pair comes once. Stops when what registered it goes away; not while its scene is paused.

Parameters

aA

One kind: a component, or a tag.

bB

The other.

callback(a: TCollisionMatched<A>, b: TCollisionMatched<B>) => void

What to do with each pair, in that order.

Example

declare const Bat: (props: { x: number }) => { hurt(damage: number): void };

export const Cave: TSceneFn = () => {
    useSpawn(Bat)({ x: 200 });
    useCollisionBetween('shot', Bat, (shot, bat) => {
        destroy(shot);
        bat.api.hurt(1);
    });
    return createScene();
};
View source · hooks/hitbox/use_collision_between.ts:42

useCollisionWithfunctionhooknacatamalon

useCollisionWith(match: M, callback: (other: TCollisionMatched<M>) => void, options: TCollisionWithOptions) => () => void

Calls callback with each thing the object being built touches, of one kind: a bullet and the enemies, the player and the coins. The piece says itself what it collides with, so a bullet and an enemy written apart need no rule in the scene that knows them both.

The kind is a component or a tag. A component (useCollisionWith(Enemy, ...)) meets the pieces useSpawn made from it, and gives each with its api typed. A tag ('damageable', or what useTag gives) meets everything carrying it, pieces of any kind and sprites, texts or panels tagged on their own, so what comes is either: 'api' in thing tells. A tag is also what a saved scene and the editor can name.

Measured as collisionBetween measures: a piece by its hitboxes that are on, or by what it draws when it has none; a sprite, text or panel by itself. Two pieces with physics bodies also touch when the simulation says so, in 2D and in 3D, so the same line works whether a hitbox or a body tells it. Run with the scene's other collisions, once its updates are over; not while it is paused. Stops when the object goes away.

Parameters

matchM

What it collides with: a component, or a tag.

callback(other: TCollisionMatched<M>) => void

What to do with each one.

Example

declare const Bat: () => { hurt(damage: number): void };

const Bullet = ({ x }: { x: number }) => {
    const self = useSelf();
    createSprite({ width: 4, height: 4, transform: { x, y: 200 } });
    useHitbox();
    useCollisionWith(Bat, (bat) => {
        destroy(self);
        bat.api.hurt(1);
    });
};
View source · hooks/hitbox/use_collision_with.ts:61

useHitboxfunctionhooknacatamalon

useHitbox(options: THitboxOptions) => THitbox

Gives this object a part that can be touched: what useCollisionWith, useCollisionBetween and collisionBetween measure it by instead of its picture. What it is ('enemy') is its tag (useTag), not the hitbox's.

Apart from the picture, because that is how these games were made. The hero's hitbox is smaller than his sprite, so a bullet that grazes his hair misses; a sword only hurts on the frames of the swing. Left without a size it is the size of the picture, frame after frame, and it is changed whenever the game likes: written to, it takes effect on the next comparison.

It is measured like the picture, in the object's own pixels, from the picture's centre: it turns, grows and moves with the object. A sword that hurts beside a body that is hurt is a piece of its own, with its own tag.

Parameters

optionsTHitboxOptions

Where it is and how big, and whether it is on.

Example

const Hero = () => {
    const keys = useKeyboard();
    createSprite({ key: 'hero', transform: { x: 40, y: 160 } });
    useTag('player').add(useSelf());
    const body = useHitbox({ width: 10 });

    useUpdate(() => {
        // A hero 24 pixels tall, crouching: 12 tall, and 6 lower so the feet stay where they were.
        const crouching = keys.isDown('ArrowDown');
        body.height = crouching ? 12 : undefined;
        body.y = crouching ? 6 : 0;
    });
};
View source · hooks/hitbox/use_hitbox.ts:59

TCollisionMatchtypenacatamalon

TCollisionMatch: string | TTag | (args: never[]) => unknown

What a collision is asked by: a tag (what useTag gives, or its name), which meets everything carrying it, or a component, which meets the pieces useSpawn made from it.

View source · hitbox/types/t_hitbox.ts:53

TCollisionMatchedtypenacatamalon

TCollisionMatched: conditional

What a collision asked by M is called with: a piece of that component, its api typed, for a component; a piece, or a sprite, text or panel tagged on its own, for a tag.

View source · hitbox/types/t_hitbox.ts:63

TCollisionWhentypenacatamalon

TCollisionWhen: 'start' | 'touching' | 'end'

When useCollisionWith calls: on the first frame two things touch, every frame they do, or on the first they no longer do.

View source · hitbox/types/t_hitbox.ts:43

THitboxtypenacatamalon

The part of an object that can be touched. Made by useHitbox, and changed whenever the game likes: smaller while crouching, off while blinking after a hit. What the object is ('enemy') is its tag, not the hitbox's: see useTag.

Measured like the object's picture, in its own pixels: it turns, grows and moves with it.

Properties

xnumber

How far its centre is moved from the centre of the picture, in the object's own pixels. 0, 0 by default.

ynumber
widthnumber | undefined

Its size, in the object's own pixels. undefined takes the picture's, frame after frame, so it follows an animation whose frames change size; a number fixes it.

heightnumber | undefined
enabledboolean

Off, nothing touches it: a character blinking after a hit, a sword between swings. true by default.

View source · hitbox/types/t_hitbox.ts:15

THitboxOptionstypenacatamalon

How a hitbox starts. All of it is optional: it begins as the picture.

Properties

xoptionalnumber
yoptionalnumber
widthoptionalnumber
heightoptionalnumber
enabledoptionalboolean
View source · hooks/hitbox/use_hitbox.ts:12