Melee combat
Skill ferret-combat · download all skills · SKILL.md
When to use#
A sword, axe or other melee fight between two bodies: a duel mini-game, a boss, sparring NPCs, a player against the AI.
The model#
A Fighter is one body's state machine (free, attack, block, blockImpact, parry, react, stagger, dodge, dead) driven by commands (move, face, run, attack, heavy, block, dodge). The player's input and FighterAI give the same commands. Its MoveSet names the clips; their timings (length, hit-start/hit-end notifies, additive) come from the library's clip set items. A Duel steps both fighters, sweeps the striking part of a weapon inside its window against the opponent's capsules and decides parry, block or hit, then the end and the restart. The rules are plain numbers: the hit shapes come from a BodyGeometry (BoneGeometry in the scene, ClipPoseGeometry in Node).
Steps#
- Make the clips: attacks that start and end in the guard, with
hit-start/hit-endnotifies; a block loop, a block impact, reactions, deaths, walks (ferret-animation,edit.clip.*). - Measure the body: capsules from the skinned mesh (
tools/fight-clips/capsules.mjs), the weapon's grip and striking part (tools/fight-clips/grip.mjs), into aBodySpec. - Build each
MoveSetfrom the clip timings of the bound clip sets; oneFighterper body. new Duel([a, b], geometry, { starts, restartAfter }); measure each attack's reach on the body (attackReach, or on the bones). Damage from outside the duel (fire, a trap):duel.scorch(fighter, damage); withoutrestartAfterthe game callsduel.restart()itself.- In the scene: one
FighterBodyper body (updatebefore the animations), thenduel.step(dt, [cmdA, cmdB])after the animations and IK. A player's command comes from input, the AI's fromFighterAI.think(dt). - React to the events (
hit,blocked,parried,over,restart): HUD, sparks, sound.
Check afterwards#
Run fights unattended in Node with ClipPoseGeometry (as packages/combat/test/demo-duel.test.ts): every fight ends, blows land only inside the windows, bodiesOverlap() stays 0, both sides win, a harder AI wins more. In the browser draw the shapes (?capsules in examples/fight) and see that the red striking part meets the green capsules where the blade meets the body.
What usually goes wrong#
- Scene and combat frames mixed: a scene point is
(-X, Y, Z)in combat and the yaw is-rotation.y(fromScene,toScene). - Unreal additive clips (hit reactions, leans) played as poses fold the body to a point: they must be marked
additivein the clip set and layered on the guard. - Hit capsules read before the frame's animations and IK: the rules see last frame's pose.
- A reach guessed instead of measured: the AI stands too far to land or too close to swing.
- One AI random source for both fighters: give each its own
seededRandomso a seed replays the same fight.
Files#
reference/combat.md— Fighters driven by the player or the AI by the same rules, weapon sweeps against hit capsules, blocks, parries, stamina, health, reactions and the end of a fight.reference/api-combat.md— every export of@ferret/combatwith signature and doc comment