Phaser AE

Vector Hit Tests

Free packv3.0.0-32DVector

Did it touch, where, and what happens next. The collision and picking maths of a line-art game, as pure functions, so a game's whole collision model can be tested without a frame ever being drawn.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 20, 2026

Vector Hit Tests: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

vectorHit

function vectorHit(): Pack<{ vecHit: VecHitApi }>

The vector-hit pack. Pure maths, so it needs no backend and runs headless.

Helpers

arcHit

function arcHit( cx: number, cy: number, r: number, from: number, to: number, px: number, py: number, tol = 6, ): boolean

Is (px, py) ON an arc — within tol of the radius AND inside the sweep?

The hit test segmented rings need: a shot only kills the shield section it actually crossed. Test in the ring's LOCAL space — subtract the centre and rotate the point by -ring.rot first, so a spinning ring needs no per-section bookkeeping.

circleHit

function circleHit(aPos: Vec2, aRadius: number, bPos: Vec2, bRadius: number): boolean

Cheap bounding-circle overlap — the pragmatic default hit test for vector games.

distToPath

function distToPath(px: number, py: number, pts: readonly VecPt[], closed = false): number

Shortest distance from (px, py) to the path — the test a STROKE needs.

An open polyline has no interior, so "did I click the line" is a proximity question, not a containment one. Closed paths include the closing edge.

Returns Infinity for an empty path and 0 for a single point at (px, py).

pointInPath

function pointInPath(px: number, py: number, pts: readonly VecPt[]): boolean

Is (px, py) INSIDE the closed path pts? Even-odd ray crossing.

Correct for concave outlines and self-intersections, which matters here because generatePolygon produces exactly that — a lumpy asteroid is not convex and a convex-hull test would grab empty space beside it.

The path is treated as closed whether or not the last point repeats the first: the j = i++ walk always includes the closing edge.

pointInPolygon

function pointInPolygon(point: Vec2, worldPoly: Vec2[]): boolean

Ray-casting (even-odd) point-in-polygon test. worldPoly MUST be in world space — call transformVerts first. Handles convex AND concave simple polygons of either winding.

polylineSegments

function polylineSegments(points: Vec2[]): Segment[]

Turn a terrain polyline (or any open path) into wall segments for sweepVsSegments.

resolveSurface

function resolveSurface(vel: Vec2, normal: Vec2, opts: SurfaceResponse = {}): Vec2

The general surface-collision response. Splits the velocity into normal and tangential parts against the outward unit normal: the normal part bounces back scaled by restitution; the tangential part is scaled by (1 - friction). bounceThreshold suppresses the bounce below a minimum approach speed so a resting body settles instead of jittering.

surfaceFriction

function surfaceFriction( vel: Vec2, normal: Vec2, coefficient: number, dt: number, gravityDir: Vec2 = { x: 0, y: 1 }, ): Vec2

Continuous ground friction for a grounded body — damps the velocity ALONG the surface, scaled by how flat it is (≈ cos of the slope): full on flat ground, less on a steep slope, none on a wall. Call each frame while grounded.

sweepVsSegments

function sweepVsSegments(prev: Vec2, cur: Vec2, segments: Segment[]): SweepHit | null

Sweep a moving point's path (prev → cur) against static wall segments and return the EARLIEST contact, or null. The core anti-tunnel test for bodies vs line geometry (terrain, cave walls).

Functions

segmentIntersection

function segmentIntersection(p1: Vec2, p2: Vec2, p3: Vec2, p4: Vec2): Vec2 | null

Intersection point of segment p1→p2 with segment p3→p4, or null if they don't cross within both spans.

Types

Segment

interface Segment

A wall segment from a to b — terrain pieces, cave walls, arena edges.

  • a: Vec2 — The start point of the wall segment.
  • b: Vec2 — The end point of the wall segment.

SurfaceResponse

interface SurfaceResponse

Tuning for resolveSurface — how a body bounces, scrubs, and settles on contact.

  • restitution?: number — 0 = no bounce (pure slide), 1 = full elastic bounce. Default 0.
  • friction?: number — 0 = frictionless, 1 = grip (kills tangential speed on contact). Default 0.
  • bounceThreshold?: number — Only bounce if the approach speed exceeds this — below it the body SETTLES. Default 0.

SweepHit

interface SweepHit

The earliest contact a swept point made: where, which segment, how far along the path (0..1).

  • point: Vec2 — The contact point, in world units.
  • index: number — The index of the segment that was hit.
  • t: number — How far along the swept path the contact is, 0..1.

Vec2

interface Vec2

A 2D point or vector. Plain data, so anything can produce one.

  • x: number — Horizontal coordinate in world units; grows to the right.
  • y: number — Vertical coordinate in world units; grows downwards (the 2D world is Y-down).

VecHitApi

type VecHitApi = typeof kit

game.vecHit — every function this pack carries, as one namespace.

VecPt

type VecPt = Vec2

A point on a path — the same shape, named for what it is used as.