Phaser AE

Melee Combat

Pro packv3.0.0-32DArcade

Close-combat timing as rules: attacks with startup, active and recovery phases, combo windows, one hit per swing, directional blocks, parries and knockback. Your game moves the fighters and draws them; the pack decides what lands.

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

Released October 14, 2026 · updated October 20, 2026

Melee Combat: 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

meleeCombat

function meleeCombat():Pack<{meleeCombat:typeof rules & typeof bot}>

Install renderer-independent melee combat policies: game.meleeCombat, the constructors and the duel bot.

Helpers

createDuelBot

function createDuelBot(melee: MeleeCombat, self: string, foe: string, options: DuelBotOptions = {}): DuelBot

A fighter for self against foe, both actors of melee.

createMelee

function createMelee(actors: readonly MeleeActor[], attacks: readonly MeleeAttack[] = duelAttacks)

A MeleeCombat for the fighters and attacks (default duelAttacks) with the default parry window of 0.14 s.

duelAttacks

const duelAttacks: MeleeAttack[]

The default attacks: a quick jab and a slow, wide cleave, each of which can follow the other.

meleeActor

function meleeActor(id: string, team: string, x: number, y: number, facing = 0): MeleeActor

A living fighter of team at (x, y), facing facing radians (0 is +x), with body radius 14, guard down and no attack.

Classes

MeleeCombat

class MeleeCombat

Timeline policy independent of movement. Call attack/block, then tick with world positions and optional occlusion.

  • readonly definitions: MeleeAttack[] — The attacks in play, cloned from the constructor's.
  • readonly actors: MeleeActor[] — The live fighters, cloned from the constructor's; tick reads and writes them.
  • readonly events: MeleeEvent[] — Events raised since the last drain.
  • constructor(actors: readonly MeleeActor[], attacks: readonly MeleeAttack[] = duelAttacks, readonly parryWindow = .14)
  • get(id: string) — The live fighter with this id. Throws Unknown fighter for an unknown id.
  • attack(id: string, move: string): { ok: boolean; reason: string; } — Start attack move for fighter id, or queue it as a follow-up when an attack is in progress and its combo window is open. Returns ok: false with a reason when the fighter is dead or stunned, or the window is closed. Starting an attack drops the guard. Throws for an unknown fighter or attack.
  • block(id: string, held: boolean) — Raise (held true) or lower the guard of fighter id. Raising it restarts blockTime; it stays down while the fighter attacks, is stunned or is dead. Throws for an unknown fighter.
  • phase(id: string) — What fighter id is doing: stunned, block, ready, or the attack's startup, active or recovery. Throws for an unknown fighter.
  • tick(dt: number, canHit: (a: MeleeActor, b: MeleeActor) => boolean = () => true) — Advance every fighter by dt seconds (0 to 1): count down stuns, advance attacks, resolve the hits that become active, and start queued follow-ups. canHit is an occlusion test and may veto a hit between two fighters. Throws for a dt outside 0 to 1.
  • drain() — Take and clear the events raised since the last drain, oldest first.

Types

DuelBot

interface DuelBot

A duel fighter.

  • next(dt: number): DuelOrders — The orders for the next dt seconds.

DuelBotOptions

interface DuelBotOptions

How the fighter fights.

  • opener?: string — The attack it opens with. Default the first defined.
  • reach?: number — Where it stands to strike, as a fraction of the opener's range. Default 0.8.
  • aggression?: number — Swings a second it starts, on average, while in reach and ready. Default 1.2.
  • combo?: number — The chance it follows a swing with the next in its combo. Default 0.5.
  • guard?: number — The chance it guards against a swing it sees coming. Default 0.6.
  • react?: number — Seconds into the other's swing before it reacts. Default 0.12.
  • seed?: number — Default 1.

DuelOrders

interface DuelOrders

What to do this step.

  • x: number — The way to walk, normalised (zero to stand).
  • y: number — The y part of the way to walk; see x.
  • facing: number — The way to face: at the other fighter.
  • attack: string | null — An attack to start or queue, or none.
  • block: boolean — Hold the guard.

MeleeActor

interface MeleeActor extends Point

A fighter: a position (world units) with a body, a facing, a guard and the attack in progress.

  • id: string — Unique fighter id.
  • team: string — Team name; fighters on the same team never hit each other.
  • r: number — Body radius in world units; added to an attack's range when it is the target.
  • facing: number — Direction the fighter faces, radians; 0 is +x.
  • alive: boolean — Whether the fighter is still in the fight; the dead neither attack nor get hit.
  • blocking: boolean — Whether the guard is up. Set with MeleeCombat.block.
  • blockTime: number — Seconds the guard has been held; a swing that lands within the parry window is a parry.
  • stunned: number — Seconds of stun left; a stunned fighter cannot attack or block.
  • attack: null | { /** Id of the attack being performed. */ id: string; /** Seconds since it began. */ elapsed: number; /** Ids of the fighters this swing has already struck, so none is hit twice. */ hit: string[]; /** The follow-up attack queued for when this one completes, or null. */ queued: string | null; } — The attack in progress, or null.

MeleeAttack

interface MeleeAttack

One attack: a timeline of startup, active and recovery, a reach, and the attacks that can follow it.

  • id: string — Unique attack name, such as jab.
  • startup: number — Seconds from the swing starting until it can hit; at least 0.
  • active: number — Seconds the swing can hit for, at least 0.001.
  • recovery: number — Seconds from the end of the active frames until the fighter is ready again; at least 0.
  • range: number — Reach in world units from the attacker's centre, at least 1; a target's own radius is added.
  • arc: number — Full width of the swing's cone, in radians, 0 to 2π, centred on the attacker's facing.
  • damage: number — Damage dealt by a clean hit; at least 0.
  • knockback: number — Size of the push on the target in world units, at least 0; a block cuts it to 15%.
  • comboOpen: number — Seconds into the attack at which the next attack can first be queued; at least 0.
  • comboClose: number — Seconds into the attack after which no follow-up can be queued; from comboOpen to the attack's total duration.
  • next: string[] — Ids of the attacks that can follow this one inside the combo window; each must be defined.

MeleeEvent

interface MeleeEvent

Something that happened in a tick, taken with MeleeCombat.drain.

  • kind: 'swing' | 'hit' | 'blocked' | 'parried' | 'complete' — swing: an attack started; hit: it landed; blocked: the target's guard took it; parried: the guard went up just in time and stunned the attacker; complete: the attack ran its course.
  • source: string — Id of the attacker.
  • target: string — Id of the fighter struck; empty for swing and complete.
  • damage: number — Damage dealt; 0 for a block, a parry, a swing or a completion.
  • knockback: Point — Push on the target in world units, directed away from the attacker; zero for swing and complete.
  • attack: string — Id of the attack concerned.