Skeleton Files - Kamzik123/AnvilToolkit-Resources GitHub Wiki
This tutorial explains how Skeleton files work in Assassin's Creed games — how bones are structured, how parent-child hierarchies define a rig, how BoneModifiers add physics, constraints, and procedural behavior, and how to add or remove bones.
The Skeleton system is part of the AnvilNext engine and the core bone structure is consistent across all AC titles. BoneModifiers have evolved over the series — see BoneModifier History Across Games for details on which modifiers are available in each title.
All examples are taken from real exported AC4: Black Flag game files using AnvilToolkit (with the Rogue schema used for the complete modifier reference, as AC4 has some modifiers removed). Examples use the Standard Exporter format throughout, as it is available for Skeletons in all games and is easier to edit (the toolkit automatically recomputes bone indices and child counts on import). A comparison section covers the Schema-Based format differences.
A note on
Pathvs ID: As with all Anvil files,Pathattributes are filled in by AnvilToolkit for human readability only. The engine locates everything by numeric ID.
- Core Concept: What is a Skeleton?
- How Addon Skeletons Work
- Anatomy of a Skeleton File
- Bone Structure
- BoneModifiers — Adding Behavior to Bones
- BoneModifier History Across Games
- Practical: How to Add a New Bone
- Practical: How to Remove a Bone
- Standard Exporter vs Schema-Based Exporter
- Quick Reference Summary
A Skeleton defines the bone hierarchy (rig) that drives a character, weapon, vehicle, or any animated object in the game. It contains:
- A flat array of bones, each with position/rotation data and a parent reference that creates the hierarchy
- A root bone pointer
- BoneModifiers attached to individual bones that add runtime behavior (physics simulation, look-at constraints, hinge joints, etc.)
- Optional metadata like IK data, body part mappings, and pose groups
Skeletons come in two main categories:
-
Full character skeletons (e.g.,
Generic_Unique_Male.Skeleton) — complete biped rigs with 70+ bones -
Addon skeletons (e.g.,
Addon_Human_Weapon_HiddenBlade.Skeleton) — small rigs that attach to a character skeleton to add weapons, accessories, or additional physics bones
Addon skeletons are the engine's way of attaching additional bones to a character without modifying the base skeleton. Weapons, hair physics, outfit-specific bones, and head setups are all addon skeletons.
The key to understanding addon skeletons is the root bone. The addon skeleton's root bone must share the same name (BoneID hash) as a bone in the main skeleton. At runtime, the engine matches the root bone by name and attaches the addon's child bones under that bone in the main skeleton's hierarchy.
The root bone itself is not added — it's already on the main skeleton. Only its children (and their descendants) get merged in.
Example: Hidden Blade Addon
The Addon_Human_Weapon_HiddenBlade.Skeleton has this structure:
Spine2 (root bone) ← Matches "Spine2" on the main skeleton
├── LeftClavicle
│ └── LeftArm
│ └── LeftForeArm
│ └── A_Hidden_Blade ← New bone (blade attachment point)
└── RightClavicle
└── RightArm
└── RightForeArm
└── A_Hidden_Blade2 ← New bone (blade attachment point)
When the engine loads this addon, it finds Spine2 on the main character skeleton and merges everything underneath it. The LeftClavicle, LeftArm, LeftForeArm bones already exist on the main skeleton — they act as a path to the new A_Hidden_Blade bones. The engine matches existing bones by name and only truly adds the bones that don't already exist (the blade attachment points).
The Addon_Human_Weapon_Sword_MainHand.Skeleton is simpler:
Spine (root bone) ← Matches "Spine" on the main skeleton
└── P_Sword_Tag ← New bone (with HingeBoneModifiers for swing physics)
└── P_LeftWaist_Tag ← New bone (secondary position)
The root bone is Spine, so the sword's physics bones get attached under the character's spine. The HingeBoneModifiers on P_Sword_Tag then simulate the sword swaying at the character's hip.
Different addon types attach at different points on the main skeleton:
| Addon Type | Root Bone | Why |
|---|---|---|
| Swords, melee weapons | Spine |
Weapons hang from the torso |
| Small weapons, pouches | Hips |
Items attached to the belt/waist |
| Hidden blades, arm addons | Spine2 |
Need to follow the arm chain from upper spine |
| Head, hair, facial bones | Spine2 |
Head hierarchy branches from Spine2 via Neck |
| Full character addons | Reference |
Complete override starting from the absolute root |
| Quad pistols, complex rigs | Reference |
Need access to multiple body parts |
When an addon skeleton contains bones with the same name as bones already on the main skeleton, the engine doesn't duplicate them — it matches them. This is how the addon "navigates" the existing hierarchy to reach the point where new bones need to be added.
For example, the hidden blade addon includes LeftClavicle → LeftArm → LeftForeArm not to create new arm bones, but to define the path from the root (Spine2) down to where A_Hidden_Blade should be attached. The existing arm bones are matched by their BoneID hash; only the truly new bones are added.
This also means an addon skeleton can add BoneModifiers to existing bones. If the addon's copy of LeftForeArm has a BallJointBoneModifier, that modifier gets applied to the main skeleton's LeftForeArm bone at runtime.
Critical: The hierarchy of matched bones in the addon skeleton must exactly match the hierarchy on the base skeleton. If the base skeleton has
LeftClavicle → LeftArm → LeftForeArm, the addon must have the same parent-child chain. If you insert a new bone betweenLeftClavicleandLeftArm(e.g., parentLeftArmto a new intermediate bone instead ofLeftClavicle), the hierarchies no longer match and the game will crash. New bones must be added as additional children, not inserted between existing bones in the chain.
The engine has a hard limit on the total number of bones per character (counted after deduplicating matched bones from all addon skeletons):
| Games | Maximum Bones | Why |
|---|---|---|
| Pre-Unity (AC1 through Rogue) | 255 | Bone index is stored as a single byte. Index 255 is reserved as an invalid/null bone marker. |
| Unity and later | 65535 | Bone index is stored as a 16-bit value. |
Exceeding this limit will crash the game. Keep this in mind when stacking multiple addon skeletons on a single character — the head addon, body addon, weapon addons, and accessory addons all contribute to the total count after deduplication.
- Choose the attachment point — decide which bone on the main skeleton should be the root of your addon
- Include the path bones — if your new bones need to be deep in the hierarchy, include the intermediate bones (they'll be matched, not duplicated)
-
Set the root bone — make the addon's
RootBonepointer reference the attachment point bone -
Set
SkeletonTypetoSKELETONTYPE_CUSTOM(8) for addon skeletons - Add your new bones as children (directly or through the path bones)
-
Reference the addon in a BuildTable — addon skeletons are loaded through the BuildTable system (typically in a BuildColumn with type
Skeleton)
Here's a simplified annotated skeleton — a sword weapon addon with 3 bones and hinge physics:
<Skeleton ID="882909070">
<!-- What kind of skeleton this is -->
<Value Name="SkeletonType" Type="UInt32">8</Value> <!-- 8 = SKELETONTYPE_CUSTOM -->
<!-- ALL bones in a flat array (hierarchy defined by Parent links) -->
<List Name="Bones">
<!-- Bone 0: Root -->
<Bone ID="882909071">
<Value Name="Name" Type="UInt32" HashName="Spine">1393476043</Value>
<ObjectPtr Name="Parent" /> <!-- No parent = root -->
...
</Bone>
<!-- Bone 1: Sword tag (with HingeBoneModifiers) -->
<Bone ID="882909072">
<Value Name="Name" Type="UInt32" HashName="P_Sword_Tag">3055624953</Value>
<ObjectPtr Name="Parent">
<Link>
<Value Name="Value" Type="UInt64">882909071</Value> <!-- Parent = Spine -->
</Link>
</ObjectPtr>
<List Name="BoneModifiers">
<!-- Physics modifiers go here -->
</List>
...
</Bone>
<!-- Bone 2: Secondary position bone -->
<Bone ID="882909077">
<Value Name="Name" Type="UInt32" HashName="P_LeftWaist_Tag">981686566</Value>
...
</Bone>
</List>
<!-- Pointer to the root bone -->
<ObjectPtr Name="RootBone">
<Link>
<Value Name="Value" Type="UInt64">882909071</Value>
</Link>
</ObjectPtr>
<!-- Root bone's default transform -->
<Vector4 Name="RootBoneDefaultLocalPosition">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
</Vector4>
<Quat Name="RootBoneDefaultLocalRotation">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>1</W>
</Quat>
<!-- Optional references (often null for addon skeletons) -->
<Reference Name="BodyPartMapping">
<FileReference Name="Value" IsGlobal="0" Path="0">0</FileReference>
</Reference>
<List Name="PoseGroups" />
<BaseObjectPtr Name="IKData" />
<ObjectPtr Name="COMData" />
<Reference Name="LookAtDataSkeletonDefinitionDefault">
<FileReference Name="Value" IsGlobal="0" Path="0">0</FileReference>
</Reference>
<Value Name="SkeletonKey" Type="UInt32">3480848966</Value>
</Skeleton>| Field | Purpose |
|---|---|
SkeletonType |
Enum identifying what kind of rig this is (see table below) |
Bones |
Flat array of all bones. Order matters — the Index field on each bone corresponds to its position here. |
RootBone |
Link pointer to the root bone in the Bones array |
RootBoneDefaultLocalPosition/Rotation |
The root bone's default rest pose transform |
BodyPartMapping |
Reference to a BodyPartMapping file (maps bones to body regions for hit detection, etc.) |
PoseGroups |
Array of pose group definitions (for animation blending) |
IKData |
Inverse Kinematics configuration |
COMData |
Center of Mass data (used for physics/balance) |
LookAtDataSkeletonDefinitionDefault |
Default look-at behavior configuration |
SkeletonKey |
A unique hash identifying this skeleton configuration |
| Type | Value | Usage |
|---|---|---|
SKELETONTYPE_BIPED |
0 | Standard male humanoid |
SKELETONTYPE_BIPED_FEMALE |
1 | Female humanoid (different proportions) |
SKELETONTYPE_QUADRUPED |
2 | Four-legged animals |
SKELETONTYPE_CARRIAGEHITCH |
3 | Carriage attachment points |
SKELETONTYPE_HITCH |
4 | Generic hitch/attachment points |
SKELETONTYPE_WAGON |
5 | Wagon rigs |
SKELETONTYPE_REINS |
6 | Rein/rope rigs |
SKELETONTYPE_CANNON |
7 | Cannon rigs |
SKELETONTYPE_CUSTOM |
8 | Custom/addon skeletons (weapons, accessories, etc.) |
Although bones are stored in a flat array, each bone has a FatherBone link that points to its parent, creating a tree hierarchy. The root bone has FatherBone set to Null.
Here's how a typical character skeleton's first few bones form a hierarchy:
Reference (root) ← FatherBone = Null
├── Hips ← FatherBone = Reference
│ ├── LeftUpLeg ← FatherBone = Hips
│ │ └── LeftLeg ← FatherBone = LeftUpLeg
│ │ └── LeftFoot ← FatherBone = LeftLeg
│ ├── RightUpLeg ← FatherBone = Hips
│ └── Spine ← FatherBone = Hips
│ └── Spine1 ← FatherBone = Spine
│ └── Spine2 ← FatherBone = Spine1
│ ├── Neck ← FatherBone = Spine2
│ │ └── Head ← FatherBone = Neck
│ ├── LeftClavicle ← FatherBone = Spine2
│ └── RightClavicle ← FatherBone = Spine2
The hierarchy is defined purely through FatherBone links — the order in the flat array doesn't define parentage, though children are typically listed after their parents.
Here's a complete bone with all fields annotated:
<Bone ID="1091164576">
<!-- Bone name as a hash — HashName shows the resolved name -->
<Value Name="Name" Type="UInt32" HashName="Hips">3738240529</Value>
<!-- Parent bone (Link to another bone's ID in this skeleton) -->
<ObjectPtr Name="Parent">
<Link>
<Value Name="Value" Type="UInt64">1091164575</Value>
</Link>
</ObjectPtr>
<!-- Mirror bone for symmetric animations (points to the opposite-side bone) -->
<ObjectPtr Name="MirrorBone">
<Link>
<Value Name="Value" Type="UInt64">1091164576</Value> <!-- Self = no mirror -->
</Link>
</ObjectPtr>
<!-- World-space position and rotation (rest pose) -->
<Vector4 Name="GlobalPosition">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
</Vector4>
<Quat Name="GlobalRotation">
<X>-0.5</X> <Y>-0.5</Y> <Z>-0.5</Z> <W>0.5</W>
</Quat>
<!-- Position and rotation relative to parent bone (rest pose) -->
<Vector4 Name="LocalPosition">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
</Vector4>
<Quat Name="LocalRotation">
<X>-0.5</X> <Y>-0.5</Y> <Z>-0.5</Z> <W>0.5</W>
</Quat>
<!-- Priority for constraint solving (higher = solved later) -->
<Value Name="SolvingPriority" Type="Byte">0</Value>
<!-- How this bone mirrors for symmetric animations -->
<Value Name="MirroringType" Type="Enum" EnumName="BoneMirroringType"
ValueName="BONEMIRRORING_PELVIS_TO_BIPED">4</Value>
<!-- Array of BoneModifiers (physics, constraints, etc.) -->
<List Name="BoneModifiers" />
<!-- Wrinkle map category (for facial/skin deformation) -->
<Value Name="WrinkleCategory" Type="Enum" EnumName="WrinkleCategory"
ValueName="None">7</Value>
<Value Name="WrinkleFactor" Type="Single">0</Value>
</Bone>Note: The
IndexandChildBoneCountfields shown in the Schema-Based format are automatically computed by the toolkit in the Standard format, so they don't appear in the XML.
| Field | Purpose |
|---|---|
BoneID |
Hash of the bone's name. The HashValue attribute shows the human-readable name (e.g., Hips, Spine, Head). Bones with unknown names show as hex (e.g., x2BA0BB9F). |
FatherBone |
Link to the parent bone's ID. Null = this is the root bone. |
MirrorBone |
Link to the bone on the opposite side of the body for mirrored animations. A bone pointing to itself means it has no mirror (center bones like Spine, Head). |
GlobalPosition |
World-space position at rest pose (X, Y, Z, W). W is typically 0. |
GlobalRotation |
World-space rotation at rest pose as a quaternion (X, Y, Z, W). |
LocalPosition |
Position relative to parent bone at rest pose. |
LocalRotation |
Rotation relative to parent bone at rest pose. |
SolvingPriority |
Order in which constraint modifiers on this bone are evaluated. 0 = default. |
MirroringType |
Defines how animation mirroring is applied at this bone (see below). |
Modifiers |
Array of BoneModifier objects attached to this bone. |
WrinkleCategory |
Which wrinkle map channel this bone drives (for facial animation). |
WrinkleFactor |
Intensity multiplier for the wrinkle effect. |
Index |
This bone's position in the flat Bones array (0-based). |
ChildBoneCount |
Total number of descendant bones (children, grandchildren, etc.). |
The MirroringType controls how the engine handles symmetric animations (e.g., walking, where left and right legs mirror each other):
| Type | Value | Usage |
|---|---|---|
BONEMIRRORING_NONE |
0 | No mirroring |
BONEMIRRORING_WORLD_TO_WORLD |
1 | Mirror in world space |
BONEMIRRORING_WORLD_TO_PELVIS |
2 | Mirror from world space to pelvis space (used on the Reference root bone) |
BONEMIRRORING_PELVIS_TO_WORLD |
3 | Mirror from pelvis space to world space |
BONEMIRRORING_PELVIS_TO_BIPED |
4 | Mirror from pelvis to biped space (used on Hips, Spine) |
BONEMIRRORING_BIPED_TO_BIPED |
5 | Mirror between matching biped bones (used on limbs, head, etc.) |
Used for facial animation to drive wrinkle/skin deformation maps:
| Category | Value | Usage |
|---|---|---|
Translation |
0 | Driven by bone translation |
AxisMagnitude |
1 | Driven by rotation magnitude around an axis |
RotationDifference_Expensive |
2 | Driven by rotation difference (costly) |
RotationYaw |
3 | Driven by yaw rotation |
RotationPitch |
4 | Driven by pitch rotation |
RotationRoll |
5 | Driven by roll rotation |
Debug |
6 | Debug mode |
None |
7 | No wrinkle effect (default for most bones) |
BoneModifiers are objects attached to individual bones that add runtime procedural behavior — physics simulation, constraints, look-at targeting, and more. They are evaluated every frame after animation playback, modifying the bone's final transform.
All BoneModifier types inherit these base fields:
<!-- In the Standard format, BlendWeight and UseModifiedBoneMatrix are attributes on the modifier element -->
<HingeBoneModifier ID="882909073" BlendWeight="1" UseModifiedBoneMatrix="false">
<!-- The Owner link is implicit — the modifier is inside the bone's BoneModifiers list -->
...
</HingeBoneModifier>In the Schema-Based format, these are separate child elements including an explicit Owner link. In the Standard format, BlendWeight and UseModifiedBoneMatrix are attributes on the modifier element, and the Owner is implicit from the parent bone.
| Field | Purpose |
|---|---|
Owner |
Link back to the bone this modifier is attached to |
BlendWeight |
How strongly the modifier affects the bone (0 = no effect, 1 = full effect). Useful for blending physics in/out. |
UseModifiedBoneMatrix |
If True, uses the matrix output by previous modifiers as input. If False, uses the original animated matrix. |
These modifiers add physics-based procedural motion to bones — things that sway, dangle, or react to gravity and wind.
Simulates a ball-and-socket joint — the bone can swing freely within a cone constraint. Used for dangling objects (small weapons, pouches, hair strands, cloth flaps).
<BallJointBoneModifier ID="22223737855" BlendWeight="1" UseModifiedBoneMatrix="false">
<Value Name="Mass" Type="Single">2.5</Value> <!-- Mass of the simulated object -->
<Value Name="CollisionRadius" Type="Single">0.04</Value> <!-- Collision sphere radius -->
<Value Name="ConstraintAngle" Type="Single">15</Value> <!-- Maximum swing angle (degrees) -->
<Value Name="ConstraintScaleY" Type="Single">1</Value> <!-- Scale the constraint ellipse on Y -->
<Value Name="ConstraintScaleZ" Type="Single">1</Value> <!-- Scale the constraint ellipse on Z -->
<Value Name="MechanicalFriction" Type="Single">1.5</Value> <!-- Joint friction (resists motion) -->
<Value Name="AirFriction" Type="Single">0.25</Value> <!-- Air drag -->
<Value Name="WindScale" Type="Single">0.35</Value> <!-- How much wind affects this bone -->
<Value Name="WindRandomFactor" Type="Single">1</Value> <!-- Randomness added to wind effect -->
<Value Name="GravityMode" Type="Enum" EnumName="GravityMode"
ValueName="GlobalGravity">0</Value> <!-- Gravity source -->
<Vector4 Name="OwnGravity">
<X>0</X> <Y>0</Y> <Z>-9.81</Z> <W>0</W> <!-- Custom gravity vector -->
</Vector4>
<Value Name="LocalYawOft" Type="Single">7.5</Value> <!-- Local yaw offset (degrees) -->
<Value Name="LocalPitchOft" Type="Single">15</Value> <!-- Local pitch offset (degrees) -->
<Value Name="LocalRollOft" Type="Single">0</Value> <!-- Local roll offset (degrees) -->
<Reference Name="CommonData">
<FileReference Name="Value" IsGlobal="0" Path="0">0</FileReference> <!-- Shared config (null) -->
</Reference>
</BallJointBoneModifier>| Field | Purpose |
|---|---|
Mass |
Heavier objects swing slower and are less affected by wind |
CollisionRadius |
Size of the collision sphere for self-collision |
ConstraintAngle |
Maximum angle (in degrees) the bone can swing from its rest pose |
ConstraintScaleY/Z |
Make the constraint cone elliptical instead of circular (values < 1 restrict that axis) |
MechanicalFriction |
How quickly motion decays due to joint resistance |
AirFriction |
How quickly motion decays due to air drag |
WindScale |
Multiplier for wind force on this bone |
WindRandomFactor |
Adds random variation to wind (0 = steady, higher = gusty) |
GravityMode |
GlobalGravity (0) = uses world gravity. LocalGravity (1) = uses OwnGravity vector. |
OwnGravity |
Custom gravity vector (only used when GravityMode = LocalGravity) |
LocalYawOft/PitchOft/RollOft |
Offset angles applied to the bone's rest orientation before simulation |
CommonData |
Optional reference to a shared BallJointCommonData file for common parameters |
Simulates a hinge joint — the bone swings on a single axis, like a door or a hanging sword. Can have multiple Hinge constraint sub-objects for different axes.
<HingeBoneModifier ID="882909073" BlendWeight="1" UseModifiedBoneMatrix="false">
<Value Name="LagFactor" Type="Single">0.4</Value> <!-- How much the bone lags behind motion -->
<Value Name="WeightFactor" Type="Single">0.4</Value> <!-- Weight influence on swing -->
<Vector4 Name="Gravity">
<X>0</X> <Y>0</Y> <Z>-65</Z> <W>0</W> <!-- Gravity force vector -->
</Vector4>
<ObjectPtr Name="GravityAssociatedBone" /> <!-- Optional: inherit gravity from another bone -->
<Value Name="WindScale" Type="Single">0.1</Value> <!-- Wind influence -->
<Value Name="AxisConstraintType" Type="Int32">0</Value> <!-- 0=AxisX, 1=AxisY, 2=AxisZ -->
<Value Name="AxisLookAtType" Type="Int32">2</Value> <!-- Which axis to align toward -->
<!-- Array of Hinge constraints (angle limits) -->
<List Name="Constraints">
<BaseObject>
<Hinge ID="882909074">
<ObjectPtr Name="ConstraintAssociatedBone" />
<Value Name="MinAngle" Type="Single">-0.7853982</Value> <!-- Min angle (radians) -->
<Value Name="MaxAngle" Type="Single">0.7853982</Value> <!-- Max angle (radians) -->
<Value Name="DeltaMinDamping" Type="Single">0.7853982</Value> <!-- Damping near min -->
<Value Name="DeltaMaxDamping" Type="Single">0</Value> <!-- Damping near max -->
<Vector4 Name="InitialGlobalPosition">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
</Vector4>
<Quat Name="InitialGlobalRotation">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>1</W>
</Quat>
</Hinge>
</BaseObject>
</List>
<Vector4 Name="LocalAxisConstraint">
<X>1</X> <Y>0</Y> <Z>0</Z> <W>0</W> <!-- Constraint axis in local space -->
</Vector4>
</HingeBoneModifier>Real-world usage: The sword addon skeleton (Addon_Human_Weapon_Sword_MainHand) uses two HingeBoneModifiers on the P_Sword_Tag bone — one for the X-axis swing (side-to-side sway, ±45°) and one for the Y-axis (forward tilt, 0° to ~10°). This creates realistic sword-on-hip motion.
| Field | Purpose |
|---|---|
LagFactor |
How much the bone trails behind character movement (0 = no lag, 1 = maximum lag) |
WeightFactor |
How much the simulated weight affects swing amplitude |
Gravity |
Gravity force vector (note: much stronger than real gravity for visual effect) |
GravityAssociatedBone |
Optional bone to inherit gravity direction from |
AxisConstraintType |
Which axis the hinge rotates around (AxisX=0, AxisY=1, AxisZ=2) |
AxisLookAtType |
Which axis points along the bone's length |
Constraints |
Array of Hinge objects defining angle limits |
LocalAxisConstraint |
The constraint axis expressed in local bone space |
Hinge sub-object fields:
| Field | Purpose |
|---|---|
MinAngle / MaxAngle
|
Angle limits in radians (e.g., 0.7854 ≈ 45°, 0.1745 ≈ 10°) |
DeltaMinDamping |
Damping force applied when approaching the minimum angle |
DeltaMaxDamping |
Damping force applied when approaching the maximum angle |
ConstraintAssociatedBone |
Optional bone that this constraint is relative to |
A more advanced hinge variant that uses target bones to define the hinge direction dynamically, rather than a fixed axis. Includes distance-based ramp-down for the effect.
| Field | Purpose |
|---|---|
HingeLocalOffsetPosition/Rotation |
Local offset from the bone's origin for the hinge pivot |
EffectRampDownMinAngle/MaxAngle |
Angle range where the effect ramps down |
EffectRampDownMinDistance/MaxDistance |
Distance range where the effect ramps down |
HingeTargets |
Array of target bones that define the hinge direction |
Simulates a pendulum — the bone swings freely under gravity like a weight on a string. Simpler than BallJoint but with different physics characteristics.
| Field | Purpose |
|---|---|
GravityAxis |
Which space to apply gravity in (GlobalGravity=0, LocalGravity=1) |
GravityScale |
Gravity strength multiplier |
Mass |
Mass of the pendulum weight |
Friction |
Mechanical friction at the pivot |
AirFriction |
Air drag on the pendulum |
Length |
Length of the pendulum arm |
FakeCentrifugalForce |
Simulated centrifugal force for fast rotations |
Adds spring-based physics to a bone — the bone oscillates around its rest position with spring dynamics. Used for bouncy or springy elements.
These modifiers constrain a bone's transform based on other bones, keeping things aligned or synchronized.
Constrains a bone's position to be a weighted blend of target bone positions. Inherits from ConstraintBoneModifier.
<!-- The base ConstraintBoneModifier provides: -->
<DynamicSmallArray Name="Targets" Type="ObjectPtr">
<!-- Link to target bone 1 -->
<!-- Link to target bone 2 -->
</DynamicSmallArray>
<DynamicSmallArray Name="KeepInitialOffset" Type="Bool">
<Bool>False</Bool>
<Bool>False</Bool>
</DynamicSmallArray>The position constraint adds no extra fields beyond the base — it simply constrains position using the target bones and their weights.
Real-world usage: In the quad pistols skeleton, position constraints keep the secondary pistol holster bones aligned between two reference points.
Constrains a bone's rotation to be a weighted blend of target bone rotations. Inherits from ConstraintBoneModifier.
<!-- Adds to ConstraintBoneModifier: -->
<Quaternion Name="Offset">( 0, 0, 0, 1 )</Quaternion> <!-- Rotation offset applied after blending -->| Field | Purpose |
|---|---|
Offset |
A quaternion offset applied after the constraint blending |
Often used together with PositionConstraintBoneModifier on the same bone to constrain both position and orientation.
A more advanced rotation constraint with inertia and damping — the constrained rotation has physical properties rather than being an instant snap.
| Field | Purpose |
|---|---|
Inertia |
How much the constraint resists changes in rotation |
Damping |
How quickly oscillations die out |
Gravity |
Gravity vector affecting the constraint |
ConstraintAssociatedBone |
The reference bone for the constraint |
Constraints |
Array of constraint definitions |
Constrains a bone to follow a spline curve defined by other bones. Used for chain-like structures (ropes, belts, hair strands).
These modifiers make a bone automatically orient toward a target — essential for head tracking, eye movement, and IK-like setups.
The most commonly used look-at modifier. Makes a bone aim one of its axes toward a target bone, with a configurable up-vector.
<MaxLookAtBoneModifier ID="4789178997" BlendWeight="1" UseModifiedBoneMatrix="false">
<!-- The bone that defines the "up" direction -->
<ObjectPtr Name="UpNode">
<Link>
<Value Name="Value" Type="UInt64">1091164576</Value> <!-- e.g., Hips -->
</Link>
</ObjectPtr>
<!-- The bone to look at -->
<ObjectPtr Name="LookAtBone">
<Link>
<Value Name="Value" Type="UInt64">1091164580</Value> <!-- e.g., LeftLeg -->
</Link>
</ObjectPtr>
<!-- Position offset from the look-at target -->
<Vector4 Name="LookAtPosOffset">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
</Vector4>
<!-- Which axis of THIS bone points toward the target -->
<Value Name="LookAtUsedAxis" Type="Enum" EnumName="MaxLookAtBoneAxis"
ValueName="AXIS_X">0</Value>
<!-- Which axis of THIS bone aligns with the up direction -->
<Value Name="UpUsedAxis" Type="Enum" EnumName="MaxLookAtBoneAxis"
ValueName="AXIS_Z">2</Value>
<!-- How the up-vector is controlled -->
<Value Name="UpNodeControl" Type="Enum" EnumName="MaxLookAtUpNodeControl"
ValueName="AXIS_ALIGNMENT">1</Value>
<!-- Source axis for alignment calculation -->
<Value Name="SourceAxis" Type="Enum" EnumName="MaxLookAtBoneAxis"
ValueName="AXIS_MINUS_Z">5</Value>
</MaxLookAtBoneModifier>| Field | Purpose |
|---|---|
UpNode |
Link to the bone that defines "up" for this look-at |
LookAtBone |
Link to the target bone to aim toward |
LookAtPosOffset |
Offset from the target bone's position |
LookAtUsedAxis |
Which axis of the modified bone points at the target (AXIS_X=0, AXIS_Y=1, AXIS_Z=2, AXIS_MINUS_X=3, AXIS_MINUS_Y=4, AXIS_MINUS_Z=5) |
UpUsedAxis |
Which axis of the modified bone aligns with the up direction |
UpNodeControl |
How the up node is used: WORLD_ALIGNMENT=0, AXIS_ALIGNMENT=1, LOOK_AT_ALIGNMENT=2 |
SourceAxis |
The axis used for the alignment calculation source |
Real-world usage: The Generic_Unique_Male skeleton uses MaxLookAtBoneModifiers extensively on the leg IK helper bones — each one aims toward the next bone in the leg chain, creating a smooth joint alignment system. The head/neck setup (Addon_CHR_P_Edward_Naked_NeckSetup) uses 5 MaxLookAtBoneModifiers to create a multi-joint head-turning system.
A variant of the look-at modifier with angle clamping — the bone looks at a target but with min/max angle limits and clamped rotation axis support.
| Field | Purpose |
|---|---|
LookAtBone |
Target bone to aim toward |
LookAtPosOffset |
Position offset from target |
MinAngle / MaxAngle
|
Angle limits for the look-at rotation |
LookAtUsedAxis |
Which axis aims at the target |
ParentUpAxis |
Up axis derived from the parent bone |
ClampedRotationAxis |
Which rotation axis to clamp |
ClampedRotationReferenceVector |
Reference vector for the clamped rotation |
Makes a bone copy the transform of another bone. Simple parent-follow behavior.
| Field | Purpose |
|---|---|
AssociatedBone |
The bone to follow/copy |
Adds roll rotation to a bone based on another bone's rotation. Used for forearm twist distribution (ulna/radius roll).
| Field | Purpose |
|---|---|
RollBoneType |
Type of roll behavior |
RollFactor |
How much of the source rotation to apply (0 to 1) |
SourceBone |
The bone to read rotation from |
Squashes/stretches a bone based on the positions of two reference bones. Used for volume preservation in deformations.
| Field | Purpose |
|---|---|
PositionInfluence1 |
How much position affects the compression |
RotationInfluence1 |
How much rotation affects the compression |
OffsetBone1 / OffsetBone2
|
The two reference bones |
RotOffset1 / RotOffset2
|
Rotation offsets for each reference |
Scales a bone along its length to stretch toward a target. Used for stretchy IK or rubber-band effects.
Drives bone rotation via a mathematical expression based on another bone's rotation. Used for mechanical linkages or complex driven-key setups.
| Field | Purpose |
|---|---|
BoneModifierUpdateMode |
When to evaluate the expression |
LinkedBone |
Source bone to read rotation from |
InitialOffset |
Starting offset value |
AxisConstraint |
Which axis to constrain the expression to |
GravityBone |
Optional gravity reference bone |
Directly copies rotation from one bone to another. Simpler than RotationExpressionModifier — pure copy with no expression evaluation.
| Field | Purpose |
|---|---|
LinkedBone |
The bone whose rotation to copy |
A marker modifier for effects bones (particle emitters, blood effects, etc.). Has no configurable properties — its presence tells the engine this bone drives visual effects.
BoneModifiers have evolved across the series. The core bone structure (hierarchy, transforms, mirroring) is the same in all games, but the available modifiers and the physics systems that power them have changed.
The original Anvil engine had the following set of modifiers:
- CompressBoneModifier — volume preservation
- FXBoneModifier — effects bone marker
- FollowBoneModifier — copy another bone's transform
- HingeBoneModifier — single-axis swing physics
- LookAtBoneModifier — look-at targeting
- PendulumBoneModifier — pendulum physics
- RollBoneModifier — twist distribution
- StretchBoneModifier — stretch toward target
BallJointBoneModifier and MaxLookAtBoneModifier did not exist yet.
AC3 significantly expanded the modifier set:
- BallJointBoneModifier — new ball-and-socket physics (replaced HingeBoneModifier as the go-to physics modifier)
- MaxLookAtBoneModifier — new look-at system with more control
- The original
LookAtBoneModifierwas renamed toAnvilLookAtBoneModifier(it still works the same way) - New modifiers added: PositionConstraintBoneModifier, OrientationConstraintBoneModifier, RotationConstraintModifier, RotationExpressionModifier, RotationPasteModifier, HingeVectorBoneModifier, SplineConstraintModifier, SpringBoxModifier
AC4 has the same modifier system as AC3 but with some modifiers removed from the schema. The examples in this tutorial are from AC4, but the Rogue schema is used for the complete modifier reference.
These titles have the complete set of all BoneModifier types listed in this tutorial. This is the golden era for BoneModifiers — the system is at its most feature-complete.
Unity introduced the early stages of Reflex — a new physics/simulation system that would eventually replace BoneModifiers. In Unity, Reflex-related data started appearing in skeleton files alongside BoneModifiers. Both systems are functional.
Origins is the transitional game. It is the first title that stopped actively using BoneModifiers in new content, preferring Reflex3 instead. However, BoneModifiers are still supported by the engine — existing modifier data still works.
Origins also added Reflex3Constraints — a new array at the end of the skeleton file that defines Reflex3 physics constraints. This is separate from and replaces the per-bone BoneModifier system for physics simulation.
Reflex3 is the primary system. BoneModifiers exist in the schema but are increasingly vestigial. Odyssey also added face builder related data to skeleton files.
Mirage still has BoneModifier definitions in its files, but they no longer function — the engine has fully transitioned to Reflex3. BoneModifier data may be present in skeleton files but has no runtime effect.
| Game | LookAtBoneModifier | AnvilLookAtBoneModifier | MaxLookAtBoneModifier | BallJointBoneModifier | Pre-AC3 Modifiers | Full Modifier Set | Reflex3 Constraints |
|---|---|---|---|---|---|---|---|
| AC1 / AC2 / Brotherhood / Revelations | Yes | -- | -- | -- | Yes | -- | -- |
| AC3 | Renamed | Yes | Yes | Yes | Yes | Full | -- |
| AC4: Black Flag | -- | Yes | Yes | Yes | Yes | Partial | -- |
| AC Rogue | -- | Yes | Yes | Yes | Yes | Full | -- |
| AC Unity | -- | Yes | Yes | Yes | Yes | Full | Early Reflex |
| AC Syndicate | -- | Yes | Yes | Yes | Yes | Full | Early Reflex |
| AC Origins | -- | Yes | Yes | Yes | Yes | Supported | Reflex3 |
| AC Odyssey / Valhalla | -- | Yes | Yes | Yes | Yes | Supported | Reflex3 |
| AC Mirage | -- | Present | Present | Present | Present | Non-functional | Reflex3 |
For modders: If you're working on AC3 through Syndicate, BoneModifiers are the way to add procedural physics and constraints. For Origins and later, BoneModifiers still technically work but new content should use Reflex3. For Mirage, BoneModifiers have no effect.
To add a bone to an existing skeleton:
Add a new <Bone> element inside the <List Name="Bones"> block:
<Bone ID="YOUR_UNIQUE_ID">
<Value Name="Name" Type="UInt32" HashName="YourBoneName">YOUR_BONE_HASH</Value>
<!-- Link to the parent bone -->
<ObjectPtr Name="Parent">
<Link>
<Value Name="Value" Type="UInt64">PARENT_BONE_ID</Value>
</Link>
</ObjectPtr>
<!-- Mirror bone (point to self if no mirror, or to the opposite-side bone) -->
<ObjectPtr Name="MirrorBone">
<Link>
<Value Name="Value" Type="UInt64">YOUR_UNIQUE_ID</Value>
</Link>
</ObjectPtr>
<!-- Transforms — set these to position the bone correctly -->
<Vector4 Name="GlobalPosition">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
</Vector4>
<Quat Name="GlobalRotation">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>1</W>
</Quat>
<Vector4 Name="LocalPosition">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
</Vector4>
<Quat Name="LocalRotation">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>1</W>
</Quat>
<Value Name="SolvingPriority" Type="Byte">0</Value>
<Value Name="MirroringType" Type="Enum" EnumName="BoneMirroringType"
ValueName="BONEMIRRORING_BIPED_TO_BIPED">5</Value>
<!-- Add modifiers here if needed, or leave empty -->
<List Name="BoneModifiers" />
<Value Name="WrinkleCategory" Type="Enum" EnumName="WrinkleCategory"
ValueName="None">7</Value>
<Value Name="WrinkleFactor" Type="Single">0</Value>
</Bone>When using the Standard Exporter format, the toolkit automatically recomputes the Index and ChildBoneCount values on import. You don't need to worry about these — just add the bone in the right position and the toolkit handles the bookkeeping.
Note: If using the Schema-Based format, you must manually update
Indexon all subsequent bones and incrementChildBoneCounton the parent bone.
Add modifiers inside the bone's <List Name="BoneModifiers"> as needed.
-
Remove the bone from the
<List Name="Bones">block -
Re-parent any children of the removed bone to the removed bone's parent (update their
<ObjectPtr Name="Parent">links) -
Update any
MirrorBonelinks that pointed to the removed bone -
Update the
RootBonepointer if you removed the root bone (point it to the new root) -
Remove any BoneModifier references in other bones that pointed to the removed bone (e.g.,
LookAtBone,UpNode,AssociatedBonelinks)
The toolkit automatically recomputes Index and ChildBoneCount on import, so you don't need to update those manually.
Warning: Removing bones that are referenced by animations, meshes, or other skeletons will cause issues. Only remove bones that you're certain are not needed.
Both formats are available for Skeletons in all AC games. This tutorial uses the Standard format in all examples. Here's how the same data looks in the Schema-Based format, for reference.
The Standard format is recommended for skeleton editing because the toolkit automatically recomputes Index and ChildBoneCount on import, and uses more readable element names.
Standard (used in this tutorial):
<Bone ID="1091164576">
<Value Name="Name" Type="UInt32" HashName="Hips">3738240529</Value>
<ObjectPtr Name="Parent">
<Link>
<Value Name="Value" Type="UInt64">1091164575</Value>
</Link>
</ObjectPtr>
<Vector4 Name="GlobalPosition">
<X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
</Vector4>
<Quat Name="GlobalRotation">
<X>-0.5</X> <Y>-0.5</Y> <Z>-0.5</Z> <W>0.5</W>
</Quat>
<Value Name="MirroringType" Type="Enum" EnumName="BoneMirroringType"
ValueName="BONEMIRRORING_PELVIS_TO_BIPED">4</Value>
<List Name="BoneModifiers" />
</Bone>Schema-Based equivalent:
<ObjectPtr ID="1091164576" Type="Bone" PointerType="Inlined">
<Hash32 Name="BoneID" HashValue="Hips">3738240529</Hash32>
<ObjectPtr Name="FatherBone" PointerType="Link">
<Link Name="FatherBone" Path="1091164575">1091164575</Link>
</ObjectPtr>
<Vector4 Name="GlobalPosition">( 0, 0, 0, 0 )</Vector4>
<Quaternion Name="GlobalRotation">( -0.5, -0.5, -0.5, 0.5 )</Quaternion>
<Enum Name="MirroringType" Type="BoneMirroringType"
Value="BONEMIRRORING_PELVIS_TO_BIPED">4</Enum>
<DynamicSmallArray Name="Modifiers" Type="ObjectPtr" />
<Unsigned8 Name="Index">1</Unsigned8>
<Unsigned8 Name="ChildBoneCount">12</Unsigned8>
</ObjectPtr>Standard (used in this tutorial):
<HingeBoneModifier ID="882909073" BlendWeight="1" UseModifiedBoneMatrix="false">
<Value Name="LagFactor" Type="Single">0.4</Value>
<Value Name="AxisConstraintType" Type="Int32">0</Value>
...
</HingeBoneModifier>Schema-Based equivalent:
<ObjectPtr ID="882909073" Type="HingeBoneModifier" PointerType="Inlined">
<ObjectPtr Name="Owner" PointerType="Link">
<Link Name="Owner" Path="882909072">882909072</Link>
</ObjectPtr>
<Float Name="BlendWeight">1</Float>
<Bool Name="UseModifiedBoneMatrix">False</Bool>
<Float Name="LagFactor">0.4</Float>
<Enum Name="AxisConstraintType" Type="BoneModifier::ModifierAxis" Value="AxisX">0</Enum>
...
</ObjectPtr>Note: In Schema-Based format, BlendWeight, UseModifiedBoneMatrix, and Owner are child elements. In Standard format, the first two are attributes on the modifier element and Owner is implicit.
| Concept | Schema-Based | Standard |
|---|---|---|
| Root element | <Object Type="Skeleton"> |
<Skeleton> |
| Bone element | <ObjectPtr Type="Bone"> |
<Bone> |
| Bone name | <Hash32 Name="BoneID" HashValue="..."> |
<Value Name="Name" HashName="..."> |
| Parent bone | <ObjectPtr Name="FatherBone"> |
<ObjectPtr Name="Parent"> |
| Modifiers array | <DynamicSmallArray Name="Modifiers"> |
<List Name="BoneModifiers"> |
| Modifier element | <ObjectPtr Type="HingeBoneModifier"> |
<HingeBoneModifier> |
| BlendWeight/UseModified | Child elements | Attributes on modifier element |
| Rotation | <Quaternion Name="...">( X, Y, Z, W )</Quaternion> |
<Quat Name="..."><X>...</X>...</Quat> |
| Vector | <Vector4 Name="...">( X, Y, Z, W )</Vector4> |
<Vector4 Name="..."><X>...</X>...</Vector4> |
| Skeleton type | <Enum Name="SkeletonType" Value="..."> |
<Value Name="SkeletonType" Type="UInt32"> |
| Owner (modifier) |
<ObjectPtr Name="Owner"> (explicit link) |
Implicit from parent bone |
| IK Data | <ObjectPtr Name="IKData"> |
<BaseObjectPtr Name="IKData"> |
| Index / ChildBoneCount | Manual (<Unsigned8> elements) |
Automatic (computed by toolkit on import) |
| Encoding | UTF-8, type="1"
|
UTF-16, type="0"
|
| Concept | What It Does |
|---|---|
| Skeleton | Defines a bone hierarchy (rig) for an animated object. Contains a flat array of bones and metadata. |
| Bone | A single joint in the hierarchy. Has position/rotation, a parent link, and optional BoneModifiers. |
| Parent / FatherBone | Link to the parent bone. Creates the tree hierarchy from the flat array. Null = root. (Parent in Standard, FatherBone in Schema-Based.) |
| MirrorBone | Link to the opposite-side bone for symmetric animations. Self-link = no mirror. |
| BoneID | Hash of the bone's name. The engine identifies bones by this hash, not by array position. |
| SkeletonType | What kind of rig: Biped, Biped Female, Quadruped, Custom (addon), etc. |
| BoneModifier | Procedural behavior attached to a bone. Evaluated every frame after animation. |
| BallJointBoneModifier | Ball-and-socket physics (dangling objects, hair, pouches). |
| HingeBoneModifier | Single-axis swing physics (swords, hanging objects). Uses Hinge sub-objects for angle limits. |
| PendulumBoneModifier | Pendulum physics (simple gravity-driven swing). |
| MaxLookAtBoneModifier | Aims a bone axis toward a target bone (head tracking, IK helpers). |
| AnvilLookAtBoneModifier | Look-at with angle clamping limits. |
| PositionConstraintBoneModifier | Constrains position to a blend of target bone positions. |
| OrientationConstraintBoneModifier | Constrains rotation to a blend of target bone rotations. |
| FollowBoneModifier | Copies another bone's transform directly. |
| RollBoneModifier | Distributes twist rotation from a source bone (forearm roll). |
| CompressBoneModifier | Squash/stretch based on reference bone positions. |
| RotationExpressionModifier | Drives rotation via mathematical expression from a source bone. |
| RotationPasteModifier | Directly copies rotation from another bone. |
| FXBoneModifier | Marker for effects bones (particles, blood). No properties. |
| BlendWeight | 0–1 multiplier on any modifier's effect. |
| SolvingPriority | Order for constraint evaluation (higher = later). |
| ChildBoneCount | Total number of descendants below this bone in the hierarchy. |