Motion matching
Same stick input, same 5 minutes of mocap.
Left: idle, walk and run clips with crossfades. Right: motion matching searches the whole take for the best next frame.
The problem
A state machine plays one clip per state and crossfades between them. The character's root follows the gameplay controller, but the clip has its own speed and its own turn rate. When they disagree, the feet skate. Starts and stops are the worst: the controller speeds up or slows down while the clip keeps its steady stride.
How it works
Motion matching skips the graph. Every frame of the mocap becomes a row of features:
- Where the character will be 20, 40 and 60 frames ahead, and which way it will face, in its own space.
- Ankle positions and velocities, and hips velocity.
Every 10 frames, or right away when the stick swings hard, the controller's predicted path plus the current pose form a query. A linear search over all rows returns the closest frame. If it is more than 20 frames from the current one, playback jumps there. Inertialization then decays the pose difference over a 0.1 s half-life, so the jump does not pop.
What I hit
My first measure of foot sliding was "ankle low and moving". It flagged swing feet too. Checking it against the raw mocap showed planted ankles sit at 11 to 12 cm, so the 13 cm threshold holds. By the same measure the raw take slides under 7 cm per foot per walking second.
The source also has foot lock: when the data says a toe is planted, two-joint IK pins it until it drifts 20 cm. It only locks when the toe is already nearly still (under 0.15 m/s). With a jump every third of a second, inertialization keeps the toe moving, so it locked only 14 times in my 14.5 s run.
My build
three.js 0.170 renders the playback. The simulation is my JavaScript port of JLPM22's Unity package. It uses the sample take that ships with it: 18,120 frames at 60 fps, 23 joints, 27 features per frame. Both characters get the same scripted stick: start, run, stop, walk back, curve, stop. The state machine uses three clips cut from the same take, with 0.25 s crossfades and no foot IK.
Over the 14.5 s run: 546 searches, 42 jumps.
Foot sliding: state machine 7.53 m, motion matching 3.72 m (4.45 m with foot lock off).
The stop after the run: 1.01 m vs 0.23 m.
Trade-offs
- Starts, stops and turns come from real mocap instead of hand-made blend trees
- Adding moves means adding data, not states
- Quality is capped by the take: when nothing in it fits the path, it jumps often and the feet slide
- Every query scans the whole database; this demo uses a linear search
- Jumps can still show up as sliding, so it needs foot lock or IK on top
When to use it
Third-person characters with lots of locomotion and a decent mocap take. For a small cast with a few hand-keyed moves, a state machine is still simpler.
Source and credits
Technique source: MotionMatching by JLPM22 (Jose Luis Ponton), https://github.com/JLPM22/MotionMatching, MIT license, commit 35fc8ee9a0fd97f12de458ea81eaef7c8219d87a. Ported to JavaScript: the simulation bone and feature extraction (future positions and directions at 20/40/60 frames, ankle positions and velocities, hips velocity, per-group normalization), the linear search with the "ignore jumps within 20 frames" rule, inertialization with decay springs, the spring character controller with its adjustment and 10 cm clamp, and foot lock (contact detection, contact inertialization, two-joint IK). No files were copied unchanged.
Motion data: the sample take jl_motionMatching_hd that ships with the same repository (MIT), used as the animation database. The state machine clips were cut from the same take.
Render framework: three.js by mrdoob and contributors, https://github.com/mrdoob/three.js, MIT license, version 0.170.0 (npm). Used only to draw the recorded playback. It is not the source of the motion matching technique.
My own code: the BVH loader, the scripted stick input, the state machine baseline (three clips, 0.25 s crossfades, no foot IK), the foot-slide measurement, the mannequin renderer, the stage and the labels. No external models. No IA model.