What it does
Every object in a 2D world carries a collision shape: a box the size of its sprite by default, or a circle, sized from
the sprite or given a radius of its own. You write rules in terms of tags. game.collide.on('bullet', 'enemy', cb)
calls cb for every overlapping pair each fixed step, after the simulation has moved things, and
game.collide.solid('player', 'wall') keeps one kind of thing out of another by moving positions, so a top-down hero
slides along a wall and enemies stop piling on top of each other. Every pair also emits a collision.hit signal, so a
sound or score system can listen without the rule knowing about it.
The callback is handed a contact, read before any separation runs: which side of the first object the second is against, and how deep. That is the answer to "am I standing on it" in a platformer and "which face did the ball hit" in a bat-and-ball game, taken on the axis of least penetration so the sign comes out right.
Fast bullets are the classic failure of overlap tests: at 3000 units a second a bullet moves 50 units a step and never
touches a 10-unit wall. Turn on continuous and each rule tests the path an object took since the last step rather
than only where it ended up, at no extra cost for anything that moved less than its own size. Rules belong to the scene
that made them and go when it ends, and a callback may safely kill either object.
Features
- Rules by tag —
onfor overlap callbacks, andsolidfor positional separation that pushes the first object, the second or both apart. - Circle and box shapes — per object, from the sprite's size and scale by default, with a radius of its own for a hit circle smaller than the art.
- Contacts —
contact(a, b)returns the side and the depth, and every rule callback receives it. - Queries —
overlaps(h, tag?)for everything touching an object now,test(a, b)for one pair, andraycastfor the nearest object along a line, optionally of one tag. - Continuous testing — a live switch that sweeps fast movers along their path, so bullets hit thin walls.
- Signals — a
collision.hitrecord per overlapping pair per step, naming the rule, for sounds and effects. - World wrap — on a wrapping world, a pair straddling the seam measures as neighbours.
- Scene-scoped rules — rules leave with their scene, so a level's rules never outlive it.
Good for
- Shooters — bullets against enemies, enemy shots against the player and power-ups on the way, with continuous testing for the fastest shots.
- Top-down action and adventure games — a hero who slides along walls and enemies that keep apart, with no physics engine.
- Arcade games where touching is the rule — maze games where eating a pellet is an overlap, and bat-and-ball games that need to know which face of a brick the ball struck.
- Triggers and pickups in any 2D game — doors, checkpoints, coins and power-ups.
- Sightlines — a raycast for the nearest wall to the right within 200 units, or whether anything stands between a guard and the player.
Make it yours
The pack has no options: the configuration is the rules you write and the shapes you give. Choose a box or a circle
per object and set the circle's radius, choose per rule who gets pushed ('a', 'b' or 'both'), and switch
continuous testing on for the levels that need it.
Two very different uses: a vertical shooter built from nothing but on rules, where bullets kill enemies, the player
collects power-ups, every hit is read from collision.hit for its sound, and continuous testing is on for the fast
shots; and a top-down dungeon where solid keeps the hero out of tile walls, enemies are solid against each other with
push: 'both', and a raycast decides whether a guard can see you.
A quick look
Rules for bullets, coins and walls, a smaller hit circle and a raycast:
game.collide.on('bullet', 'enemy', (b, e) => { kill(b); e.fsm.go('dying'); });
game.collide.on('player', 'coin', (_, c) => { kill(c); score += 5; });
game.collide.solid('player', 'wall'); // positional separation, no physics
game.collide.solid('enemy', 'enemy', { push: 'both' });
game.spawn({ ..., collide: { shape: 0, radius: 11 } }); // 0 circle, 1 box; radius 0 = from size
const hit = game.collide.raycast(x, y, dx, dy, 400, 'wall'); // nearest, or null
Works with
core-2dandcore-vector— both include this pack, so most 2D games have it already.physics-2d— requires this pack and runs before it, so overlap rules see bodies at their settled positions.tiles-2d— spawns a tilemap's collision as tagged boxes ('wall'by default) that these rules see.grid-move— four-way lane movement on a grid, which asks the game what a wall is.collision-3d— the same calls for a 3D world.
Good to know
- There is no velocity, mass or bounce:
solidonly moves positions. For pushes with momentum composephysics-2d, and never usesolidon its bodies, since the solver owns their separation. - Boxes are always axis-aligned: rotation is ignored.
raycasttests bounding boxes, so a circle counts as its square: right for a sightline, not for a billiards shot.- Continuous testing samples the path in up to 256 steps, so a fast enough mover can still step over something small enough, and the callback fires at the first sampled overlap rather than the exact moment of impact.
- An object spawned inside a callback is not seen to overlap anything until the next step.






