Phaser AE

Corridor shooter enemies

Pro packv3.0.0-0ArcadeNew

The enemies of a first-person corridor game. They guard, patrol, notice you, hunt you through the doors and fight, and every kind is a few lines of configuration.

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

Released October 24, 2026

Corridor shooter 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

fpsEnemies

function fpsEnemies(): Pack<{ fpsEnemies: typeof kit; }>

Compose the first-person enemies under game.fpsEnemies.

Helpers

createHorde

function createHorde(config: HordeConfig, world: EnemyWorld): Horde

Make the enemies of a map, given its EnemyWorld (map.enemyWorld()). Throws, naming the field, on a config it cannot use.

Classes

Horde

class Horde

The enemies of a first-person game on any map that offers an EnemyWorld (cell-dungeon, sector-map): they stand guard or patrol, see the player within their sight and field of view, react after a moment, hear shots through open doors, chase along the map's routes opening doors on the way, aim and attack (a hitscan shot whose chance falls with distance, a bite, or a projectile for the game to fly), flinch when hurt, turn on another kind that hurts them, and fall with their drops, making their sounds as they go. Only a few aim at once. Time moves only through tick. Get one from createHorde.

  • constructor(config: HordeConfig, world: EnemyWorld)
  • get state(): HordeState — A copy of the state.
  • list(): Enemy[] — Every enemy, dead ones too (copies).
  • get alive(): number — Enemies still standing.
  • def(id: string): EnemyDef | undefined — An enemy kind's definition.
  • body(def: string): EnemyBody — The body an enemy kind moves as. Throws for an unknown kind.
  • spawn(def: string, x: number, z: number, yaw = 0, mode: 'stand' | 'patrol' = 'stand', ambush = false): string — Put an enemy of kind def at x, z facing yaw, standing or patrolling, on the floor there (a flyer above it). ambush leaves it deaf to shots until it sees the player or is hurt. Returns its id.
  • hear(x: number, z: number, radius: number): HordeEvent[] — A noise at x, z heard radius units away through open space and open doors: every enemy in reach that is not deaf starts hunting.
  • hitTest(x: number, z: number, yaw: number, range: number, eye?: number, ignore?: string): { id: string; dist: number } | null — The nearest live enemy a ray from x, z along yaw meets within range units before a wall: its id and the distance, or null. What a hitscan pellet hits. With the shooter's eye height eye, an enemy counts only when the shooter can see it (the shot aims up and down by itself, as the classic games did). ignore skips one enemy (the shooter, when an enemy fires).
  • hurt(id: string, damage: number, by?: string): HordeEvent[] — Hurt an enemy: it may flinch, and it falls at 0 hit points with its drops. by is who did it: another enemy's id, or nothing for the player. Hurt by the player, it hunts the player; hurt by another kind (with infighting on), it turns on that one.
  • facing(id: string, x: number, z: number, frames = 8): number — Which of frames (1 to 16, default 8) sprite angles shows enemy id from a viewer at x, z: frame 0 when it faces the viewer, counting round as it turns away to its right. For eight-angle billboards.
  • control(id: string, puppet: boolean): void — Hand an enemy to something else, or take it back: a puppet keeps its hit points, pain, death and sounds, but the horde neither moves it nor attacks with it. fps-bosses drives its bosses this way.
  • place(id: string, x: number, z: number, yaw: number, y?: number): void — Put a (puppet) enemy somewhere: position, facing, and feet height (default the floor there), counting the walk.
  • heal(id: string, hp: number): void — Give an enemy back up to hp hit points, never above its kind's full health.
  • wake(id: string): HordeEvent[] — Wake an enemy, as if it had seen the player.
  • tick(dt: number, player: EnemyTarget): HordeEvent[] — Run every enemy on by dt seconds (0 to 1) against the player.
  • restore(state: HordeState): void — Put back a saved state. Throws on one that names unknown kinds.

Types

Enemy

interface Enemy

One enemy.

  • id: string — Unique id, <def>-<n>.
  • def: string — Its kind.
  • x: number — Position in world units.
  • z: number — Position in world units.
  • y: number — Height of its feet (a flyer's, in the air).
  • yaw: number — The engine's yaw it faces.
  • hp: number — Hit points left.
  • mode: EnemyMode — What it is doing.
  • timer: number — Seconds left in a timed mode (aim, pain) or before it reacts.
  • cooldown: number — Seconds until it may attack again.
  • alerted: boolean — Whether it knows where the player is.
  • react: number — Seconds until it reacts to having seen the player, or −1 when it has not.
  • route: EnemyWaypoint[] — The points it is walking to, next first.
  • replan: number — Seconds until it plans its route again.
  • walked: number — Distance walked, for the presenter's walk cycle.
  • target: string | null — Who it is after: null for the player, or another enemy's id after that one hurt it.
  • noise: number — Seconds until its next ambient sound.
  • deaf: boolean — Placed in ambush: deaf to shots until it sees the player or is hurt.
  • puppet: boolean — Driven by something else (a boss controller): the horde leaves its movement and attacks alone.

EnemyAttack

interface EnemyAttack

How an enemy attacks.

  • kind: 'hitscan' | 'melee' | 'projectile' — 'hitscan' (a shot that hits or misses at once), 'melee' (a bite or swing within reach) or 'projectile' (something thrown that the game flies).
  • damage: [number, number] — Damage, [min, max] whole numbers from 0 to 10000.
  • range: number — Reach in world units, 0.5 to 128.
  • cooldown: [number, number] — Seconds between attacks, [min, max] from 0.05 to 30.
  • accuracy?: number — Hitscan: chance to hit at point blank, 0 to 1; default 0.8. It falls with distance to a third at full range.
  • windup?: number — Seconds spent aiming before the attack lands, 0 to 5; default 0.3 (0.1 for melee).
  • projectile?: { speed: number; radius: number } — Projectile: speed in units per second and radius in units.

EnemyBody

interface EnemyBody

The body the world moves: its size and how it climbs.

  • radius: number — Radius in world units.
  • height: number — Height in world units.
  • step: number — Highest step up, and deepest step down, it takes.
  • fly: boolean — Whether it flies over steps and drops.

EnemyDef

interface EnemyDef

A kind of enemy.

  • id: string — Unique id.
  • hp: number — Hit points, 1 to 100000.
  • speed: number — Walking speed in units per second, 0 to 20.
  • chaseSpeed?: number — Chasing speed; default speed.
  • radius?: number — Body radius in units, 0.1 to 4; default 0.3.
  • height?: number — Body height in units, 0.1 to 20; default 1.6. Its eye is at 85% of it; it needs this much headroom.
  • step?: number — Highest step it walks up, and the deepest drop it will walk off, in units, 0 to 20; default 0.5.
  • fly?: number — A flyer: how high above the floor it hovers, in units; it crosses steps and drops freely. Default none.
  • sight?: number — How far it sees, in units, 0 to 256; default 24.
  • fov?: number — Half-angle it sees across, radians, 0 to π; default π/2 (all round its front).
  • reaction?: [number, number] — Seconds from first seeing the player to reacting, [min, max] from 0 to 10; default [0.2, 0.7].
  • attack: EnemyAttack — Its attack.
  • pain?: number — Chance of flinching when hurt and not killed, 0 to 1; default 0.5.
  • painTime?: number — Seconds a flinch lasts, 0 to 5; default 0.35.
  • score?: number — Points for killing it, 0 to 1000000; default 0.
  • drops?: string[] — Things it leaves where it falls, by name; default none.
  • deaf?: boolean — Deaf to shots (it still sees); default false.
  • sounds?: EnemySounds — Its sounds, by name; default none.
  • ambient?: [number, number] — Seconds between its idle and active sounds, [min, max] from 0.5 to 600; default [4, 10].

EnemyMode

type EnemyMode = 'stand' | 'patrol' | 'chase' | 'aim' | 'pain' | 'dead'

What an enemy is doing.

EnemySounds

interface EnemySounds

The sounds an enemy makes, by name, for the game to play where the enemy is (HordeEvent sound). Any may be left out. idle and active repeat at the ambient interval: idle while it has not noticed the player, active while it hunts.

  • see?: string — When it notices the player (by sight, a shot heard or being hurt).
  • attack?: string — When it attacks.
  • pain?: string — When it flinches.
  • death?: string — When it dies.
  • idle?: string — Now and then while it waits, unaware.
  • active?: string — Now and then while it hunts.

EnemyTarget

interface EnemyTarget

The player, as the enemies see them.

  • x: number — Position in world units.
  • z: number — Position in world units.
  • y?: number — Height of their feet, for line of sight over steps and through openings; default none (sight is flat).
  • height?: number — Their height; default 1.6.
  • alive: boolean — Whether there is anyone to attack.
  • moving?: boolean — Whether they are moving (harder to hit); default false.

EnemyWaypoint

interface EnemyWaypoint

A point on an enemy's way, and the door to open before going on, if any.

  • x: number — Where, in world units.
  • z: number — Where, in world units.
  • door?: string — A door to open first, by the world's id for it.

EnemyWorld

interface EnemyWorld

What enemies need of the map they live on. cell-dungeon and sector-map each give one (map.enemyWorld()); any map that answers these can host a horde.

  • sees(ax: number, az: number, bx: number, bz: number, ay?: number, by?: number): boolean — An unbroken line from a to b; with heights, through openings in 2.5D maps.
  • move(x: number, z: number, dx: number, dz: number, body: EnemyBody, y?: number): { x: number; z: number; y: number } — Move a body by dx, dz, sliding along walls; where it ends up and the floor under it. y is its feet now.
  • route(fx: number, fz: number, tx: number, tz: number, body: EnemyBody): EnemyWaypoint[] | null — The way from one point to another for this body, next point first, opening unlocked doors; null when there is none.
  • hearing(x: number, z: number, radius: number): (x: number, z: number) => boolean — Who hears a noise at x, z that carries radius units through open space and open doors.
  • wallDistance(x: number, z: number, yaw: number, max: number, y?: number): number | null — How far a flat ray goes before a wall or closed door, or null within max. y is its height, for 2.5D maps.
  • doorShut(door: string): boolean — Whether a door on a route is still shut.
  • openDoor(door: string): boolean — Start a door opening; true when it was shut and now opens.
  • floorAt(x: number, z: number): number — The floor height at a point.
  • settle?(x: number, z: number): { x: number; z: number } — Where a patroller that has just turned should stand: a cell map's cell centre. Default where it is.

HordeConfig

interface HordeConfig

The enemies of one map.

  • defs: EnemyDef[] — The kinds of enemy, 1 to 64.
  • maxAttackers?: number — Most enemies aiming at once, 1 to 64; default 3.
  • damageScale?: number — Multiplies every enemy's damage, 0 to 10; default 1.
  • infighting?: boolean — An enemy hurt by another of a different kind turns on it; default true.
  • seed?: number — Seed, 0 to 4294967295; default 1.

HordeEvent

type HordeEvent = | { type: 'alerted'; id: string } | { type: 'aiming'; id: string } | { type: 'attack'; id: string; kind: 'hitscan' | 'melee'; hit: boolean; damage: number; target: string | null } | { type: 'projectile'; id: string; x: number; y: number; z: number; yaw: number; pitch: number; speed: number; radius: number; damage: number; target: string | null } | { type: 'pain'; id: string } | { type: 'died'; id: string; x: number; y: number; z: number; drops: string[]; score: number; by: string | null } | { type: 'door'; id: string; door: string; x: number; z: number } | { type: 'sound'; id: string; sound: string; x: number; y: number; z: number }

What the enemies did. target is null when the player is the target, else an enemy's id.

HordeState

interface HordeState

Everything that changes; a save holds exactly this.

  • enemies: Enemy[] — Every enemy, dead ones too.
  • serial: number — Enemies spawned so far, for ids.
  • rng: number[] — The random stream.