Custom Creature: the animation clips
Authoring locomotion and attack clips for a creature, exporting them so the Modding Kit reads them correctly, and the one tag whose absence crashes the game everywhere at once.
Part of the Custom Creatures guide. Read the skeleton page first: if you author against the wrong rig, nothing on this page will save you.
Version
Measured against Bannerlord v1.4.8.
Locomotion clips are authored in place
A walk cycle has zero net root travel. The feet cycle, the body stays at the origin.
The engine supplies the translation itself, from the movement system. If you bake root motion into a walk, the engine adds its translation to yours and the creature moves at double speed while its feet skate.
anf_displace_position is not root motion from your keys: it moves the agent by the vector in the
clip's displacement clip usage, up to that usage's end progress, and without the usage the engine
reads null. Vanilla sets it on 337 clips, all with that usage, such as death_fall_front. Keep it
off walks and runs: their travel is the bip_mov_ik or quad_movement usage's loop displacement.
See the clip inspector.
quad_movement, or: the six-hour crash
This is the single most expensive lesson on these pages, so it goes near the top.
Every gait clip in a movement_system="quadrupedal" action set must carry the quad_movement clip
usage. Walks, runs, strafes, turns in motion, jumps. TAOM's reverse engineering of v1.5.3 found ten
places that read it with no null check. Step points make the footsteps (with make_walk_sound); they
are not shown to be needed against the crash, and 42 of the 142 vanilla clips with quad_movement have
none.
Without it:
- The clip compiles fine. No error.
- It even plays correctly on a detached, non-mount agent. So it tests clean.
- Then a quadrupedal action set measures it, builds a null native gait structure, and the first
Skeleton.TickAnimationsorGetWalkSpeedLimitOfMountabledereferences it. - Access violation at offset
+0x10, in every mount context at once: the inventory thumbnail, the character tableau, and mission deployment. That offset was seen on v1.4.x; the v1.5.3 offset has not been observed.
The secondary fingerprint, if you are staring at a log: resolving an unbound action through the
poisoned set returns a runtime-synthesised garbage name, something shaped like
1002467048434979358_0.
In the Modding Kit, quad_movement is a CLIP USAGE, not a Flag
It is in the collapsed Clip usages section, below the Flags list. People look for a checkbox in Flags, do not find one, and conclude the tag does not exist.
make_walk_sound is a Flag. Step points are a third, separate field: they are the footstep
timing fractions, and unset they read as -1, -1, -1, -1.
Attack, hit and death clips correctly do not carry quad_movement. Only movement clips.
Per-category recipe
A working quadruped's clips on its own skeleton, by category:
| Clip category | Flags | Clip usages |
|---|---|---|
| gait (walk, run, gallop, turn, strafe) | make_walk_sound |
quad_movement, plus step points for footsteps |
| attack | client_prediction, lock_movement, enforce_all |
none |
| death | make_bodyfall_sound, client_prediction, do_not_keep_track_of_sound, enforce_all, update_bounding_volume |
none |
| rear | lock_movement, enforce_lowerbody |
none |
Gallop runs no longer get cyclic: vanilla's carry only make_walk_sound, and no native requirement
was found. A clip on the horse rig should copy vanilla's horse recipe:
| Vanilla clip | Priority | Flags | Blend in / out |
|---|---|---|---|
horse_kick (attack) |
34 | enforce_lowerbody, enforce_all |
0.2 / 0.4 |
horse_rear |
74 | lock_movement, enforce_lowerbody, update_bounding_volume, ignore_slope |
0.3 / 0.3 |
horse_hit_from_front |
2 | enforce_lowerbody |
0.2 / 0.4 |
Every vanilla horse clip read carries enforce_lowerbody. Why priority decides what shows in battle:
the clip inspector.
Gait theory, or: why it looked wrong when everything was technically correct
Three things that made TAOM's creatures stop looking uncanny.
Elephants use a four-beat lateral sequence (left hind, left fore, right hind, right fore). Never a trot, never a pace. Their mass means there is no aerial phase at all, so the "fast" gait is a faster amble, not a different gait. Animating an elephant like a large horse reads as wrong immediately and it is hard to say why.
Spiders use an alternating tetrapod. Four legs down, four legs moving, alternating sets.
A spider idle is a braced, splayed stance, not a walk-in-place. An idle built by damping a walk looks like an animal treading water.
Amplitude edits have to be anchored to the planted-frame pose, or the stance foot floats. Phase shifts are a cyclic time offset and are safe to apply freely.
Exporting the clip
Armature only, and the armature object and its data are both renamed <skeleton>_notused.
object_types={'ARMATURE'}
add_leaf_bones=False
primary_bone_axis='Y', secondary_bone_axis='X'
axis_forward='-Y', axis_up='Z'
bake_anim=True
bake_anim_use_all_bones=True
bake_anim_use_nla_strips=False
bake_anim_use_all_actions=False
bake_anim_force_startend_keying=True
bake_anim_step=1.0
bake_anim_simplify_factor=0.0
What shipped clips from three separate creatures agree on, and what they do not:
| Property | Verdict |
|---|---|
UpAxis / FrontAxis / CoordAxisSign = 2 / 1 / -1 |
match this |
Model node types: Null 1 + LimbNode |
match this |
| Bones the Kit drops on import | must be 0 |
| TimeMode | not load-bearing. Shipped clips use both 24 and 30 fps |
| Clip flags count | not load-bearing. One shipped attack clip has zero flags and works |
Two traps in the export settings themselves:
bake_anim_use_all_bones=Falsedoes not reduce the exported bone set. Blender bakes the whole armature wheneverbake_animis on. The flag name is misleading.- Any
*_nub_notusedbone in the animation FBX is a difference from every shipped clip. Strip them before authoring.
Byte size is a reliable empty-bake detector
A healthy clip export is megabytes and takes seconds. An export that produced no keyframes comes out at roughly 0.06 MB in under 0.01 s. If your file is tiny, the bake found nothing.
On recent Blender this is usually because renaming edit bones does not rewrite slotted-action fcurve data paths. Lowercase your bones and the fcurves still point at the old names, so the bake is empty.
Compiling in the Kit
Import as a Skeleton Animation, set its Owner Skeleton, then create an Animation Clip:
Source 1 = 1 and Source 2 = the master's last frame, because frame 0 is the rest frame
(the export mapping); Duration must come out
above zero. Set Loading Type to Always keep in memory (0) on any clip you bind: the conservative
choice, not a proven requirement
(why). Keep the name to 63
characters (the Kit warns Could not set fixed-size(64) string). Every field:
the clip inspector.
A new clip starts at priority 0 with no flags, and may never show in battle
The model viewer plays it unopposed; in battle, by TAOM's reading, locomotion takes the channel
back. TAOM's war ram logged 1,300 head-butts at priority 0 and 1,005 at 34 with no visible head
drop; it now copies horse_kick's flags and blends too, not yet seen in battle. Copy the nearest
vanilla clip's whole recipe and judge it in a battle.
Do not rename a clip inside the Modding Kit
Renaming corrupts it. The Kit keeps resolving the old name, the inspector reports
Size in KB = 0, it refuses to save, the model viewer draws a scrambled pose, and the renamed
file can vanish outright. Restarting the tools clears that state, but nobody has recorded
checking a clip renamed in the Kit afterwards: no rename inside the Kit, with or without a
restart, is known to give a usable clip.
Instead: create the clip on the default new_animation_clip name, set its source range and
flags, save and close the Kit. Then rename the clip item inside its _anm.tpac, not just
the file: the game registers a clip by that stored name. TAOM's optional
rename_anim_clip_tpac.py
does it, keeping the item's GUID, and writes <name>_anm.tpac; a hex editor can make the same
two-field edit (the byte layout). Reopen
the Kit.
The clip name itself does not need a particular prefix form. Bare names, single-prefixed and
double-prefixed all work; the <skeleton>| prefix you see on compiled clips comes from the Kit's
Owner Skeleton field, not from your take name.
Riders are not the creature skeleton
A mounted rider is the standard 28-bone human_skeleton. It is not your creature's rig.
Mounted rider poses are authored as human clips and bound in the as_human_warrior action set under
act_<mount>_* names mapping to rider_<mount>_* clips. At runtime the engine parents the rider to
the mount's rider_sit_bone.
Sit bones from shipped creatures, as a sanity check on your own:
| Mount | rider_sit_bone |
|---|---|
| horse | horsespine2 |
| warg | Spine1_M |
| spider | chest_m |
| elephant | Spine1_05 |
That leading space in the elephant's bone name is real
Bone names exported from some tools carry leading spaces, and the XML must reproduce them
exactly. A bone name that does not match resolves to index -1, silently, and a -1
rider_sit_bone seats the rider at the world origin. The symptom is "my rider is floating at
his own feet".
You do not have to author rider clips at all if an existing creature's fit close enough. TAOM's
spider reuses the warg's rider_warg_* clips.
Diagnosing a clip that looks right in Blender and wrong in game
Do these in order. Each is cheap and eliminates a whole class.
- Read the compiled master back in TpacTool and compare each bone's rotation with the FBX's local and the engine's rest local. If limbs play another limb's motion, match each slot at frame 0 to the bone whose rest it equals; a mismatch means the node order is wrong (the export mapping, point 4).
- Check frame 0 is the rest pose when the limbs move right but the body floats or the feet skate.
- Render a FRONT view. A yaw is almost invisible from the side, and it is very easy to spend hours looking only at side views. Assert on bone direction vectors, not head positions.
- Count the bones the Kit will drop. Any
*_nub_notusedis a difference from every shipped clip. - Diff the FBX globals against a shipped clip: UpAxis, FrontAxis, CoordAxisSign, node types.
- Compare the rig against the engine skeleton. Parenting and bone axis are the two things a mesh FBX gets wrong with no visible symptom.
A verification that cannot fail is not a verification
Three hypotheses were "refuted" during one TAOM session by tests structurally incapable of detecting the fault, and two turned out to be real problems.
Deleting the _nub_notused bones in Blender and seeing no pose change proves nothing: the nubs
sit coincident with their child carrying identity rotation, so removing them is a no-op by
construction. Re-parenting a bone in Blender and seeing no change fails the same way. And a
bone's head position cannot detect a rotation about its own origin, so head checks are blind to
exactly the error being hunted.
Before trusting a negative result, ask what the test would show if the hypothesis were true. If the answer is "the same thing", the test is worthless.
Pick the right reference creature
Compare your creature mount against a single-creature mount: a warg, a spider, an elephant.
Do not use a multi-creature rig such as a chariot as your reference. A two-horse-plus-cart rig is its own skeleton asset whose horse-named bones are not the horse skeleton, and they sit 90 degrees off every mesh rig. That looks exactly like a smoking gun and sent two rounds of TAOM's work in the wrong direction.
Measured against same-shaped creatures, a creature's animation FBX uses the same bone convention as its own mesh FBX: median differences of 6.79 and 16.83 degrees, which are rest-pose differences, not a 90 degree flip.
The export mapping
Measured by reading Kit-compiled masters back against the FBX and the engine's rest frames, on
human_skeleton (52 clips) and horse_skeleton:
- The Kit stores an FBX's bone-local transforms as they are, with no axis conversion. So the
armature you export from must carry the engine's bone frames: each bone's
matrix_localequal to the accumulated rest frame (tail = head + Y column,align_roll(Z column)). The bones then draw sideways in Blender, harmlessly. Export withprimary_bone_axis='Y',secondary_bone_axis='X'. - The root is stored as its FBX world pose turned 180 degrees about Z, with the armature object's transform applied on top. Keep the object at identity and bake the 180 degree turn into the pose.
- Frame 0 is the rest frame. The root position track is stored relative to it, and every vanilla
master opens on rest with its clip at Source 1 = 1. Open on a posed frame and the pelvis offset is
lost: a hunched character stands too high and its feet skate. Key rest at frame 0, motion from
frame 1 (the site's animation notes agree). Vanilla
walks carry one root track, the pelvis bob; drop a pack's root travel and give it to the engine as
the
bip_mov_ikusage's loop displacement. - Tracks are stored in FBX node order, and the engine reads slot i as bone i of the skeleton's
list. Blender writes nodes depth-first.
human_skeleton's list is depth-first;horse_skeleton's is not (neck last), so a horse clip from the true hierarchy plays the tail on the neck. Export from a hierarchy whose depth-first walk equals the list (horseneck1underhorsetail3, as TaleWorlds' own horse and goat FBX have it), with each local relative to its engine parent. - The take name is the master's identity. The master is named after the FBX take (the Blender
action). A re-import under the same take keeps its GUID and its clips. A
.001suffix makes a new master with an empty skeleton, and the old clip loses its animation.
TpacTool's FixBoneForBlender export has bone frames 90 to 180 degrees off the engine's: use its
meshes, not its bones. Optional helpers, where Blender and TpacTool do the same by hand: TAOM's
transfer_clip_to_engine_rig.py
moves a clip onto an engine-frame rig with points 2 to 4 handled;
read_anim_keyframes_tpac.ps1
reads a master back (set -TpacToolBin, -NativeDir and -OutDir; the defaults are one machine's).