Phaser AE

Platform enemies

Pro packv3.0.0-32DArcade

Enemy behaviours and spawners for side-view arcade platform games: patrollers, ladder-aware chasers, wanderers, rolling barrels, hoppers, creatures that flip and recover, and the pipes and throwers that keep them coming. Each kind is data, and the director drives them on the same level and the same rules as the player.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 14, 2026 · updated October 19, 2026

Platform enemies: 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

platformEnemies

function platformEnemies(): Pack<{ platformEnemies: typeof api }>

Install the renderer-independent platform enemies API.

Helpers

alienKind

function alienKind(o: KindOptions = {}): EnemyKind

Space Panic alien: wanders floors and ladders toward the player; cannot jump. Estimates.

barrelKind

function barrelKind(o: KindOptions & { ladderChance?: number; steer?: number; plungeChance?: number } = {}): EnemyKind

Donkey Kong barrel: rolls along girders (~1 px/frame, estimate), falls off the ends with a small bounce and reverses, and at each ladder top may roll down the ladder. The chance rises when Mario is below (the arcade steers barrels toward the player's joystick; modelled as a target-below bonus). Passes broken ladders.

bouncerKind

function bouncerKind(o: KindOptions = {}): EnemyKind

Bubble Bobble Monsta style flyer: flies diagonally and ricochets off walls, floors and ceilings. Estimates.

bubbleWalkerKind

function bubbleWalkerKind(o: KindOptions & { skill?: number } = {}): EnemyKind

Bubble Bobble walker (Zen-chan style): chases along floors, jumps up through one-way platforms toward a higher player, walks off edges toward a lower one, wraps from the bottom to the top. Estimates.

chaserKind

function chaserKind(o: KindOptions & { skill?: number } = {}): EnemyKind

A generic ladder-aware chaser for custom levels: shortest routes over floors and ladders, re-planned often.

createEnemies

function createEnemies(o: EnemiesOptions): EnemyDirector

Create an enemy director over a walker world.

fighterFlyKind

function fighterFlyKind(o: KindOptions = {}): EnemyKind

Mario Bros Fighter Fly: hops in fixed arcs and can only be flipped while touching the floor. Estimates.

fireballKind

function fireballKind(o: KindOptions & { ladderChance?: number; bias?: number } = {}): EnemyKind

Donkey Kong fireball / Firefox: wanders along girders, turns at edges, climbs ladders (not broken ones), drifting toward Mario. Speeds are estimates (fireballs are slower than Mario).

guardKind

function guardKind(o: KindOptions & { skill?: number; strategy?: "scan" | "lode-runner" } = {}): EnemyKind

Lode Runner guard: runs slower than the runner, climbs, hangs and drops. strategy: 'scan' (default) chases with a row-scan heuristic after Doug Smith's original; strategy: 'lode-runner' uses the 1983 decision itself (lodeRunnerMove: same-floor dash, then left/right floor scans rating ladders and drops 100 + rows above the runner, 200 + rows below him, distance on his row), falls centred into holes and steps off ladders into the air. Speeds are estimates (speed scales them).

lodeRunnerCell

function lodeRunnerCell(map: PlatformMap, b: Pick<WalkerBody, "x" | "feet">): { col: number; row: number }

The grid cell the original code tracks: the column under the centre, the row whose cell the feet are nearest.

lodeRunnerIntent

function lodeRunnerIntent(map: PlatformMap, me: WalkerBody, move: LodeRunnerMove, occupied: (col: number, row: number) => boolean = () => false): Partial<WalkerIntent>

Turn a decision into a walker intent: step sideways (climbers first reach a row line), climb or drop, centring on the column before entering a ladder. A move into another guard's cell becomes a stop.

lodeRunnerMove

function lodeRunnerMove(map: PlatformMap, me: WalkerBody, target: WalkerBody, occupied: (col: number, row: number) => boolean = () => false): LodeRunnerMove

Decide a guard's next move toward a target with the 1983 rules. occupied(col, row) reports another guard in a cell (guards stand on guards, and never step into a guard's cell).

patrollerKind

function patrollerKind(o: KindOptions & { width?: number; height?: number } = {}): EnemyKind

A generic patroller for custom levels: walks a ledge and turns at edges and walls.

shellcreeperKind

function shellcreeperKind(o: KindOptions = {}): EnemyKind

Mario Bros Shellcreeper: walks, falls off ledges, wraps, flips on one bump, recovers faster. Estimates.

sidestepperKind

function sidestepperKind(o: KindOptions = {}): EnemyKind

Mario Bros Sidestepper: the first bump angers it (faster), the second flips it. Estimates.

Types

BehaviourContext

interface BehaviourContext

What a custom behaviour sees.

  • world: WalkerWorld — The walker world.
  • map: PlatformMap — The level.
  • random(): number — Seeded random number in [0, 1).
  • dt: number — Fixed step.
  • time: number — World time.
  • target(enemyId: string): WalkerBody | null — The nearest target body to an enemy, or null.

BounceMove

interface BounceMove

Fly diagonally and ricochet off walls, floors and ceilings (needs a flight profile). Bubble Bobble's Monsta.

  • type: "bounce" — Bounce.
  • dy?: -1 | 1 — Initial vertical direction (-1 up, 1 down). Default 1.
  • edges?: "wrap" | "bounce" — At the map's left and right edges: 'wrap' with the map (default) or 'bounce' back (use a profile with wrap: false).

CustomMove

interface CustomMove

A behaviour registered by name with the director (behaviours option); the extension point for rule packs.

  • type: "custom" — Custom.
  • name: string — Registered behaviour name.
  • params?: Record<string, number | string | boolean> — Parameters passed to the behaviour.

EmitterDef

interface EmitterDef

A spawner: pipes, a thrower, an oil drum.

  • id: string — Unique id.
  • points: EmitterPoint[] — Spawn points; cycled or picked at random.
  • pick?: "cycle" | "random" — 'cycle' through points or pick at 'random'. Default 'cycle'.
  • kinds?: { kind: string; weight: number }[] — Weighted kinds chosen at random.
  • sequence?: string[] — Kinds in order (used before kinds); loops when exhausted.
  • every?: number | number[] — Seconds between spawns: one number or a cycling table. Default 3.
  • jitter?: number — Random extra seconds 0..jitter per interval. Default 0.
  • first?: number — Seconds before the first spawn. Default 0.
  • max?: number — Most live enemies from this emitter. Default unlimited.
  • total?: number — Stop after this many spawns. Default unlimited.
  • manual?: boolean — Only spawn on emit(). Default false.
  • active?: boolean — Start active. Default true.

EmitterPoint

interface EmitterPoint

A spawn point.

  • x: number — Centre x.
  • feet: number — Feet y.
  • facing?: -1 | 1 — Facing of the spawned enemy. Default 1.

EnemiesOptions

interface EnemiesOptions

Options for createEnemies.

  • world: WalkerWorld — The walker world the enemies live in.
  • kinds: Record<string, EnemyKind> — Enemy kinds by name.
  • emitters?: EmitterDef[] — Emitters.
  • exits?: ExitDef[] — Exits.
  • seed?: number — Seed. Default 1.
  • behaviours?: Record<string, EnemyBehaviour> — Custom behaviours by name.
  • targets?: string[] — Target body ids.
  • attach?: boolean — Register as a world controller. Default true.
  • id?: string — Controller id. Default 'enemies'.
  • speedScale?: number — Global speed multiplier (difficulty, hurry-up). Default 1.

EnemiesSnapshot

interface EnemiesSnapshot

Serialisable director state (the walker world snapshots the bodies).

  • enemies: EnemyState[] — Enemies.
  • emitters: { id: string; timer: number; index: number; spawned: number; point: number; active: boolean }[] — Emitters.
  • rng: RngState — Random stream state (the kernel's rng.snapshot()).
  • seq: number — Id counter.
  • targets: string[] — Targets.
  • speedScale: number — Global speed.
  • events: EnemyEvent[] — Undrained events.

EnemyBehaviour

interface EnemyBehaviour

A custom behaviour. Keep state in enemy.memory (serialisable) so snapshots capture it.

  • init?(ctx: BehaviourContext, enemy: EnemyState, body: WalkerBody, params: Record<string, number | string | boolean>): void — Called once at spawn.
  • think(ctx: BehaviourContext, enemy: EnemyState, body: WalkerBody, params: Record<string, number | string | boolean>): Partial<WalkerIntent> — Return the intent for this step.
  • sense?(ctx: BehaviourContext, enemy: EnemyState, body: WalkerBody, params: Record<string, number | string | boolean>): void — After movement: react to sensors.

EnemyDirector

interface EnemyDirector extends WalkerController

The enemy director: a walker controller plus spawning, hits and queries.

  • readonly enemies: EnemyState[] — Copies of every enemy.
  • enemy(id: string): EnemyState | null — One enemy, or null.
  • view(): readonly Readonly<EnemyState>[] — The live enemy list, not copies: cheap enough to read many times a step. Never keep it across steps or write to it; to remove enemies while going through it, go through a copy of the array ([...view()]).
  • peek(id: string): Readonly<EnemyState> | null — One enemy's live state, or null; see view.
  • count(kind?: string): number — Live enemies (not waiting in an exit), optionally of one kind.
  • setTargets(ids: string[]): void — Set the target body ids.
  • spawn(kind: string, at: { x: number; feet: number }, o?: { facing?: -1 | 1; emitter?: string }): string — Spawn an enemy at a feet position. Returns its id.
  • emit(emitter: string, kind?: string): string | null — Spawn from an emitter now (its next point and kind unless kind is given). Returns the id or null when capped.
  • setEmitter(id: string, active: boolean): void — Turn an emitter on or off.
  • hit(id: string): "flipped" | "angered" | "unflipped" | "ignored" — Hit an enemy: flips, angers, rights a flipped one, or is ignored.
  • bump(col: number, row: number, radius?: number): { id: string; result: "flipped" | "angered" | "unflipped" | "ignored" }[] — Hit every enemy standing on a cell (and radius cells either side): a floor bumped from below.
  • pow(): { id: string; result: "flipped" | "angered" | "unflipped" | "ignored" }[] — Hit every grounded enemy (a POW block).
  • flip(id: string, seconds?: number): void — Flip an enemy for a time regardless of hit rules.
  • recover(id: string): void — Recover a flipped enemy now (no speed-up).
  • suspend(id: string, reason: string): void — Hand an enemy to a rule pack (trapped in a bubble or a hole): no intents until resumed.
  • resume(id: string): void — Return a suspended enemy to its behaviour.
  • remove(id: string, reason?: string): void — Remove an enemy and its body.
  • touching(bodyId: string, inset?: number): EnemyState[] — Enemies overlapping a body (any status except waiting).
  • setSpeedScale(k: number): void — Global speed multiplier.
  • setSpeed(id: string, k: number): void — One enemy's own speed multiplier (anger, hurry-up), kept across the director's speed updates.
  • remember(id: string, key: string, value: MemoryValue): void — Write a memory value.
  • drain(): EnemyEvent[] — Events since the last drain.
  • snapshot(): EnemiesSnapshot — Serialisable state.
  • restore(s: EnemiesSnapshot): void — Restore a snapshot (restore the walker world too).

EnemyEvent

type EnemyEvent = | { kind: "spawned"; id: string; enemy: string; x: number; y: number; emitter: string } | { kind: "turned"; id: string; facing: -1 | 1 } | { kind: "ladder"; id: string; dir: "up" | "down" } | { kind: "jumped"; id: string } | { kind: "flipped"; id: string } | { kind: "angered"; id: string; hits: number } | { kind: "unflipped"; id: string } | { kind: "recovered"; id: string; level: number } | { kind: "exited"; id: string; exit: string } | { kind: "reentered"; id: string; emitter: string } | { kind: "suspended"; id: string; reason: string } | { kind: "resumed"; id: string } | { kind: "removed"; id: string; reason: string }

Events from drain().

EnemyKind

interface EnemyKind

An enemy kind: a movement profile, a locomotion strategy and optional modifiers.

  • profile: WalkerProfile | WalkerProfileInput — Walker profile (complete or partial over the defaults).
  • move: MoveSpec — Locomotion.
  • hop?: { every: number; jitter?: number; onlyMoveInAir?: boolean } — Hop every few seconds while grounded (Fighter Fly); onlyMoveInAir stands still between hops.
  • jumpUp?: { rows: number; chance: number; every?: number } — Jump up through one-way floors toward a higher target (Bubble Bobble enemies).
  • stun?: StunSpec — Flip and recover.
  • speedUp?: { alone: number; factor: number } — Speed multiplier when this many or fewer enemies remain (Mario Bros' last enemy).
  • exits?: boolean — Use the director's exits (pipes). Default true.
  • tags?: string[] — Extra tags on the walker body.

EnemyState

interface EnemyState

Public enemy state.

  • id: string — Walker body id.
  • kind: string — Kind name.
  • status: EnemyStatus — Status.
  • hits: number — Hits taken toward the next flip.
  • level: number — Recoveries so far (speed level).
  • speed: number — Speed multiplier from anger and recoveries.
  • timer: number — Seconds left flipped / waiting.
  • emitter: string — Emitter it came from ('' if spawned directly).
  • reason: string — Why suspended ('' otherwise).
  • memory: Record<string, MemoryValue> — Behaviour memory.
  • plan: RouteStep[] — Current route (pursuit).

EnemyStatus

type EnemyStatus = "active" | "stunned" | "suspended" | "waiting"

Enemy status.

ExitDef

interface ExitDef

A zone that swallows enemies and returns them from an emitter point after a delay (Mario Bros pipes).

  • id: string — Unique id.
  • zone: { x: number; y: number; w: number; h: number } — Rectangle (top-left and size).
  • to: string — Emitter to re-enter from.
  • point?: number — Index of the emitter point to use; default cycles.
  • delay?: number — Seconds out of play. Default 1.

IdleMove

interface IdleMove

Do nothing (items carried by belts and moving platforms, pies, cement tubs).

  • type: "idle" — Idle.

KindOptions

interface KindOptions

Options shared by the kind presets.

  • tile?: number — Level tile size; speeds and sizes scale by tile / 8. Default 8.
  • speed?: number — Speed multiplier (difficulty, level). Default 1.

LodeRunnerMove

type LodeRunnerMove = "left" | "right" | "up" | "down" | "stop" | "fall"

A Lode Runner guard decision.

MemoryValue

type MemoryValue = number | string | boolean | null

Serialisable memory values for behaviours.

MoveSpec

type MoveSpec = PatrolMove | PursueMove | WanderMove | RollMove | BounceMove | IdleMove | CustomMove

Locomotion strategy.

PatrolMove

interface PatrolMove

Walk one way and turn at walls (and at edges when edges is 'turn'). Mario Bros enemies, generic patrols.

  • type: "patrol" — Patrol.
  • edges?: "turn" | "fall" — At an edge: turn back, or walk off and fall. Default 'turn'.
  • turnChance?: number — Chance per second of turning round for no reason. Default 0.

PursueMove

interface PursueMove

Chase a target over floors, ladders, bars and drops.

  • type: "pursue" — Pursue.
  • strategy?: "path" | "scan" | "greedy" | "lode-runner" — 'path' (shortest route over the map), 'scan' (Lode Runner style: scan the row for the ladder or drop that gets closest to the target's row), 'greedy' (head for the target, climb when a ladder points the right way), 'lode-runner' (the 1983 guard decision itself, re-evaluated every step: see lodeRunnerMove). Default 'path'.
  • replan?: number — Seconds between re-plans. Default 0.25.
  • skill?: number — 0..1: chance per re-plan of making the good decision; otherwise wander for that interval. Default 1.
  • climb?: boolean — Use ladders (if the profile can climb). Default true.
  • hang?: boolean — Use bars (if the profile can hang). Default true.
  • drop?: boolean — Walk off edges and drop from bars to reach the target. Default true.
  • maxFall?: number — Longest drop in rows the route may plan. Default unlimited.
  • jumpUp?: number — Rows the enemy may jump up through one-way platforms (needs a jumping profile). Default 0.
  • unreachable?: "toward" | "wander" | "nearest" — 'path' with no route to the target: head toward its x on the current floor ('toward', default), roam floors and ladders until a route opens ('wander'), or plan to the nearest reachable cell on the target's row and press toward it from there ('nearest': chasers walk into the pits a player digs around himself).

RollMove

interface RollMove

Roll along floors, fall off ends, maybe take ladders down (Donkey Kong barrels).

  • type: "roll" — Roll.
  • ladderChance?: number — Base chance of going down a ladder top it rolls over. Default 0.25.
  • steer?: number — Extra chance when the target is below (the arcade's steering). Default 0.5.
  • reverseOnLand?: boolean — Reverse after landing from a fall. Default true.
  • afterLadder?: "reverse" | "keep" | "toward-target" — Direction after leaving a ladder. Default 'reverse'.
  • plungeChance?: number — Chance at spawn of plunging straight through one-way floors to the bottom (wild barrels). Default 0.
  • plungeHop?: number — While plunging: hop up at this speed (units/s) each time a one-way floor is crossed, so the barrel bounces down the screen (Donkey Kong wild barrels). Default 0 (fall straight through).
  • plungeSpeed?: [number, number] — While plunging: horizontal speed range (units/s) chosen at spawn and at every floor crossed. Default [0, 0].
  • plungeAim?: "random" | "target" | "proportional" — While plunging: direction of that speed: 'random', 'target' (toward the target), or 'proportional' (the target's x distance per second, capped at the range's maximum). Default 'random'.
  • plungeUntil?: number — Stop plunging once the feet pass this world y: the next floor is landed on and rolled along. Default: plunge until a solid floor.
  • plungeExit?: -1 | 0 | 1 — Facing after a plunge ends (-1, 1, or 0 to keep). Default 0.

StunSpec

interface StunSpec

Flip-over / stun-and-recover rules (Mario Bros).

  • hits?: number — Hits needed to flip (Sidestepper: 2). Default 1.
  • recover: number — Seconds flipped before recovering.
  • groundedOnly?: boolean — Only hittable while touching the ground (Fighter Fly). Default false.
  • recoverSpeedUp?: number — Speed multiplier gained on each recovery. Default 1.25.
  • maxLevel?: number — Most recoveries that speed it up. Default 3.
  • angerOnHit?: number — Speed multiplier from a hit that does not flip (an angry Sidestepper). Default 1.3.
  • unflipOnHit?: boolean — Hitting a flipped enemy rights it. Default true.
  • pop?: number — Upward pop speed when flipped. Default 0.

WanderMove

interface WanderMove

Roam floors and ladders, drifting toward the target (Donkey Kong fireballs, Space Panic aliens).

  • type: "wander" — Wander.
  • ladderChance?: number — Chance of taking a ladder met on the way (once per ladder visit). Default 0.5.
  • bias?: number — 0..1: how often decisions favour the target's direction. Default 0.5.
  • turnChance?: number — Chance per second of turning round. Default 0.3.
  • edges?: "turn" | "fall" — At an edge: turn or fall. Default 'turn'.