Native Control Rig Animation
Native Control Rig Animation documentation.
UE-MCP can author and modify animation through Unreal's Control Rig and Sequencer APIs. The reliable workflow is data first: describe the pose as component-space anatomical constraints, discover how the selected rig reaches those constraints, write normalized quaternion keys, then validate the baked bones and capture a few exact frames for human review.
Do not reuse control names, Euler angles, or axis signs from another skeleton. Those are rig-specific observations, not an animation recipe.
The npm package ships the same operating rules as the
ue-mcp-animation agent skill,
so ue-mcp init can install them with the other bundled workflow skills.
Compatibility
| Capability | Engine support |
|---|---|
begin_control_rig_edit, read_control_rig_edit, apply_control_rig_edits, bake_control_rig_edit | UE 5.8 only; older engines return unsupported_engine_version and do not fall back to raw bone tracks |
analyze_animation | Cross-version; it uses the native animation APIs available in the engine against which the bridge was compiled |
configure_ik_rig, configure_ik_retargeter, and contact_lock inside apply_control_rig_edits | UE 5.8 only; older engines return unsupported_engine_version |
| Legacy IK Rig and IK Retargeter create/read actions | See each action in the tool reference; their older-engine support is unchanged |
The editing loop is native. execute_python and Computer Use are not part of
it. If a required datum is missing, add a native UE-MCP read or write action
instead of making an escape hatch part of the workflow.
Author IK and retarget context
Control Rig edits operate most predictably when the selected skeleton already
has explicit chains, goals, and a solver. On UE 5.8, configure an existing IK
Rig with configure_ik_rig rather than writing reflected asset properties:
animation(
action="configure_ik_rig",
rigPath="/Game/Rigs/IK_<character>",
retargetRoot="pelvis",
rootMotionBone="root",
chains=[...],
fullBodyIK={rootBone:"pelvis", goals:[...]}
)The operation validates every named bone, chain ancestry, goal, solver
connection, and numeric setting before saving. read_ik_rig is the required
round-trip check: verify its preview mesh, roots, chains and goal assignments,
goals, exclusions, solver stack, and full-body effector settings. Do not treat
a chain carrying a goal name as proof that the goal or solver exists.
Configure retargeting with configure_ik_retargeter:
animation(
action="configure_ik_retargeter",
retargeterPath="/Game/Rigs/RTG_<source>_<target>",
sourceRig="/Game/Rigs/IK_<source>",
targetRig="/Game/Rigs/IK_<target>",
sourcePreviewMesh="/Game/Meshes/SK_<source>",
targetPreviewMesh="/Game/Meshes/SK_<target>",
ensureDefaultOps=true,
autoMapMode="exact",
forceRemap=true,
chainMappings=[...],
pose={side:"target", name:"<named_pose>", create:true, autoAlign:"chain_to_chain", rotationOffsets:[...]}
)The UE 5.8 operation stack must receive both rigs; setting only the top-level retargeter reference is insufficient. Auto-map first, apply deliberate manual overrides second, then create and adjust a named pose. A whole-pose auto-align resets that pose before the manual rotation offsets are applied. Read the retargeter back and verify its per-operation rig assignments and mappings, current/named pose deltas, preview meshes, and processor validation messages.
Partial mapping is valid when the target owns extra twist, metacarpal, or accessory chains. Record the intentionally unmapped chains and require all motion-critical chains instead of blindly requiring every target chain.
The authoring loop
1. Establish a per-character Control Rig baseline
begin_control_rig_edit edits through a Control Rig; it does not invent one.
Before authoring the first clip for a new project or character, search for an
existing rig bound to the target mesh/skeleton. Inspect it with
read_control_rig_hierarchy and read_control_rig_graph. A usable baseline
must expose the controls needed by the intended motion, a Forward Solve that
drives the bones, and a Backward Solve that can initialize those controls from
source animation without a pose jump.
If no suitable rig exists, create that baseline first. UE 5.8's bundled
Control Rig toolset is available through the animation category: start with
epic_create, import the production skeleton with
epic_import_bones_from_asset, add the intended controls with
epic_add_control, and add inverse initialization with
epic_add_backward_solve_graph; then use the remaining Control Rig graph,
node, and link actions and save the exact rig with asset(epic_save_assets).
epic_create alone creates no imported bones, authored controls, or solver
wiring.
Build only the controls and solvers required by the character's current
animation work. Verify the saved hierarchy and graph, then round-trip an
unchanged source clip through Backward Solve, Forward Solve, and bake. Bone
transforms must remain within the project's tolerance before the rig is trusted
for production edits.
Do not treat matching bone or control names as compatibility proof. Do not create a new rig per animation; the verified character rig is the reusable authoring foundation and baked AnimSequences remain the runtime output.
2. Orient and create an immutable session
Start with project(action="get_status") and verify the active project and
editor connection. Resolve the source AnimSequence, skeletal mesh, skeleton,
and Control Rig before writing.
Create a versioned LevelSequence and binding tag:
animation(
action="begin_control_rig_edit",
sequencePath="/Game/AnimationWork/LS_<motion>_V001",
skeletalMeshPath="/Game/<mesh>",
sourceAnimationPath="/Game/<source_anim>",
rigMode="asset",
controlRigPath="/Game/<control_rig>",
layered=false,
startFrame=0,
endFrame=<exclusive_end>,
displayRate=<fps>,
bindingTag="<motion>.v001",
onConflict="error"
)Use rigMode="fk" only when Unreal's generated UFKControlRig is the intended
editing surface. Use rigMode="asset" for a project's authored Control Rig.
startFrame is inclusive and endFrame is exclusive, so [0, 91) permits
keys at frames 0 through 90.
The source must be a non-additive AnimSequence compatible with the selected
mesh. Flatten an additive clip against its intended base pose first. The
session's layered option controls whether the source track remains active
under the Control Rig layer; it does not supply an additive source's base pose.
Sequencer compensates a finite non-zero AnimSequence RateScale, so the session
maps the raw source timeline from start to end exactly once without changing the
source asset. A zero RateScale is rejected because it has no invertible time
mapping.
Every material iteration gets a new session path, binding tag, and eventual
output path. Keep onConflict="error" while developing. skip is suitable
only for a deliberately idempotent replay; begin and bake never overwrite an
existing asset.
3. Read controls before choosing them
Call read_control_rig_edit on the candidate controls at the rest, transition,
peak, opposite peak, and final frames. Read both spaces:
animation(action="read_control_rig_edit", sequencePath=<session>,
bindingTag=<tag>, controlNames=[...], frames=[...], space="local")
animation(action="read_control_rig_edit", sequencePath=<session>,
bindingTag=<tag>, controlNames=[...], frames=[...], space="global")local is relative to the control's rig parent. global is the Control Rig
hierarchy's global space, which normally corresponds to skeletal-mesh
component space. It is not actor world space. World-space review must also
compose the skeletal mesh component or actor transform.
Control metadata reports:
controlType,transformControl, andanimatableenumName,enumPath, andenumOptionswith each option's name, display name, and integer value- transform samples, or typed bool, float/scale-float, integer, and enum values
Never write a control with animatable=false. Use set_bool, set_float, or
set_int for scalar controls. For an enum, pass an exact integer listed in
enumOptions; do not infer it from the option's position or label.
4. Solve anatomy in component space
Define observable targets before changing controls. For an arm gesture, solve proximal to distal:
- Shoulder and upper arm: place the arm's reach and elevation without collapsing the shoulder into the torso.
- Elbow: treat the elbow as a pole target. Preserve a plausible bend and a stable elbow plane; do not let the elbow flip between frames.
- Forearm and wrist: make the elbow-to-wrist vector point where the action requires. A wave, for example, needs the forearm to rise rather than merely moving the upper arm sideways.
- Palm: align a rig-discovered palm normal toward the intended viewer or interaction target. Establish which local hand axis represents that normal from data; names such as X, Y, or Z are not anatomical facts.
- Secondary motion: add the wrist oscillation only after the raised pose is correct. Ease the entry and exit, and preserve fingers unless the gesture explicitly needs them changed.
IK is useful when the hand target and elbow pole are exposed and well behaved. FK is valid when those controls are absent or their orientation contract is unclear, but still judge the result using component-space bones and landmarks. Do not tune a wrist control in isolation and assume the arm chain followed.
What transfers to other rigs and motions
The loop transfers; the rig mapping does not. Keep the same discover, constrain, solve, key, bake, and validate stages, then choose landmarks suited to the motion:
- Legs use pelvis/hip placement, a knee pole, a foot target and orientation, ground contact, and foot-slip measurements.
- Spine, neck, and head motion uses a component-space arc, twist distribution, and an aim or gaze direction with per-joint limits.
- Tails, tentacles, ropes, and other chains use a target curve, segment-length preservation, bend limits, and phase-delayed keys along the chain.
- Prop, socket, and mechanical animation uses pivot axes, attachment transforms, contact/clearance constraints, and the same quaternion continuity checks.
- A retargeted character repeats target-side control, axis, scale, and limit discovery before edits; source-rig constants are not portable evidence.
This makes the method reusable across skeletons and animated hierarchies while keeping the only unavoidable custom data small: control/bone mapping, local axes and signs, mirrored scale, joint limits, and the motion's constraints.
5. Probe mirrored and ambiguous axes
Never obtain right-side rotations by negating left-side Euler values. A right-side control may inherit mirrored axes or negative scale, so the same local rotation can have a different anatomical meaning.
For every unfamiliar rig, and separately for each side when needed:
- Read the relevant controls in local and global space, preserving their full
translation, rotation, and scale. Use
get_bone_transformsin local and component space for reference-pose chain orientation. - In a disposable versioned session, apply a small positive and negative rotation around one local axis at one fixed frame. Change only one axis per probe.
- Bake and run
analyze_animationfor the shoulder, upper arm, forearm, hand, and a palm/finger landmark. Record the observed component-space movement. - Build the axis/sign mapping for that rig and side. Preserve any negative scale from the read transform. Repeat the probe after changing rigs or retargeting to a materially different hierarchy.
This small probe is cheaper and safer than correcting a full clip built on an assumed axis convention.
6. Apply absolute quaternion keys
Use set_keys for reproducible transform authoring:
{
"op": "set_keys",
"control": "<discovered_control>",
"space": "local",
"keys": [
{
"frame": 12,
"transform": {
"translation": { "x": 0, "y": 0, "z": 0 },
"rotationQuaternion": { "x": 0, "y": 0, "z": 0, "w": 1 },
"scale": { "x": 1, "y": 1, "z": 1 }
}
}
]
}The numbers above illustrate the payload shape only; read the real base transform first. The rules are:
- Keys are full, absolute transforms with strictly increasing, unique frames.
- Quaternions must be finite and normalized. UE-MCP preserves shortest-arc continuity; use quaternions rather than interpolating Euler angles.
- Preserve read-back translation and scale unless the motion intentionally changes them. This is especially important for mirrored/negative-scale controls.
- A source animation baked into Control Rig may already contain a key on every frame. In that case, write the edited control on every frame of the affected interval; sparse keys do not replace intervening source keys.
- Apply related controls and scalar switches in one
apply_control_rig_editscall. The batch is prevalidated, transacted, read back, and undone if application or readback fails.
Read the same frames again after applying. Check both local continuity and the global/component anatomical targets before baking.
Contact constraints
Use contact_lock when a known Control Rig driver must hold a control, bone, or
socket at a fixed mesh-component-space transform across an inclusive interval:
{
"op": "contact_lock",
"control": "<keyable_ik_driver>",
"drivenReference": "<bone_or_socket>",
"startFrame": 12,
"endFrame": 38,
"target": {
"translation": { "x": 0, "y": 0, "z": 0 },
"rotationQuaternion": { "x": 0, "y": 0, "z": 0, "w": 1 }
},
"blendInFrames": 4,
"blendOutFrames": 4,
"stabilizeControls": ["<pole_or_secondary_control>"],
"positionToleranceCm": 0.1,
"rotationToleranceDegrees": 0.5
}Read the target from the source or session; the identity values above show only the payload shape. The session must contain one source skeletal-animation section. The bridge samples that AnimSequence at each mapped Sequencer frame, measures the bone/socket offset from the driver, and solves dense component-space driver keys. Smooth edge weights blend into and out of the lock, and at least one frame must remain fully constrained.
FK bones commonly use skeleton translation retargeting, which discards their
authored translation keys during AnimSequence playback. For an FK
drivenReference contact on such a bone, the bridge instead resolves the
driver-to-reference descendant chain, runs a per-frame FABRIK position solve,
and writes local rotation-chain keys while preserving the source local bone
translations. The result reports solver=fk_rotation_chain. This special FK
path requires a driven bone and does not accept stabilizer controls; use an
asset Control Rig when the contact targets a socket or also needs a pole or
secondary stabilizer. A translation-only target leaves the driven control's
orientation unkeyed; the end control joins the keyed chain only when
target.rotationQuaternion is supplied.
The apply call transactionally reads back the driver and stabilizer keys. With
drivenReference, contactQa.verification is
bake_and_analyze_required: the composed layered bone/socket result is not
claimed before export. Bake to a new AnimSequence, run analyze_animation on
every constrained frame, and compare the driven reference with the target. If
it exceeds the motion's acceptance tolerance, reject that output and revise the
driver, stabilizers, or rig mapping. Without drivenReference, the driver
itself is constrained and its residual is checked during apply.
This is deliberately a generic contact primitive, not a foot-specific macro. It works for hands on props, planted feet, held tools, mechanical linkages, and other contacts when the rig exposes a driver with the required degrees of freedom. The caller still discovers the correct driver and optional pole/stabilizer, handles foot roll or pelvis compensation when the rig needs them, and verifies the baked affected and unaffected bones. It does not model collision, friction, joint limits, foot roll, or pelvis compensation.
7. Bake to a new asset
animation(
action="bake_control_rig_edit",
sequencePath=<session>,
bindingTag=<tag>,
outputAssetPath="/Game/AnimationWork/A_<motion>_V001",
frameRate=<fps>,
reduceKeys=false,
createLink=false,
onConflict="error"
)The source animation and LevelSequence are not overwritten. Key reduction and
Sequencer/AnimSequence links are not supported by this workflow, so omit
reduceKeys and createLink or pass false.
Deterministic validation
Run analyze_animation on both the source and baked output with the same mesh,
bones, and frames:
animation(
action="analyze_animation",
assetPath=<baked_anim>,
skeletalMeshPath=<mesh>,
boneNames=[<root>, <pelvis>, <edited_chain>, <feet>, <opposite_side>],
frames=[...],
loop=false,
outputDirectory="<motion>/v001"
)The native result and optional manifest.json / samples.ndjson contain exact
local and component transforms. The built-in summary reports numeric integrity,
invalid-transform count, root displacement and maximum root speed, selected-bone
bounds, and optional loop-seam root/joint errors. The manifest also records
centimeter units and Unreal's +X forward, +Y right, +Z up convention.
Timing metadata is explicit: durationSeconds is the raw sequence duration,
rateScale is the AnimSequence asset rate, and effectiveDurationSeconds is
the raw duration divided by the rate magnitude. Each notifies record includes
rawTriggerTimeSeconds and its rate-scaled effectiveTriggerTimeSeconds. A
zero asset rate reports null effective times because playback does not advance.
Derive motion-specific assertions from the samples rather than screenshots:
- wrist height relative to shoulder and the elbow-to-wrist direction
- elbow angle, elbow-plane change, and per-frame joint angular change
- palm-normal alignment using the axis established by the probe
- wrist speed/acceleration and deliberate wave direction changes
- root, feet, opposite side, fingers, and other supposedly untouched bones remaining within the fixture's tolerance of the source
Always inspect the first frame, transition frames, extrema, last visible frame, and any frame with the largest numeric delta. Screenshots are review evidence; the sampled transforms are the source of truth.
Exact fixed-frame Unreal capture
Capture through native UE-MCP calls, without Computer Use or Python:
-
editor(action="open_asset", assetPath=[baked_anim]). -
editor(action="find_object", className="AnimSingleNodeInstance", nameContains="AnimPreviewInstance", world="any"). If several objects are returned, choose the one under the currentAnimationEditorPreviewActor(or constrainouterPath) rather than a stale preview instance. -
Freeze and seek atomically with
editor(action="invoke_object_functions"):{ "calls": [ { "objectPath": "<resolved preview instance>", "functionName": "SetPlaying", "args": { "bIsPlaying": false } }, { "objectPath": "<resolved preview instance>", "functionName": "SetPosition", "args": { "InPosition": 1.1333333333, "bFireNotifies": false } } ] }Compute
InPositionexactly asframe * rateDenominator / rateNumerator; the decimal above is only an example payload. -
Open the asset again to focus its Slate window, then call
editor(action="capture_screenshot", target="window", filename=[png]). -
Repeat the same camera and window setup for start, raised pose, both motion extrema, and end. Keep clean images beside the numeric analysis artifacts.
V&V fixtures
Before treating a rig or workflow as production-ready, retain these small, versioned fixtures:
| Fixture | Proves |
|---|---|
| From-scratch gesture over a neutral source pose | Per-rig discovery, typed controls, dense quaternion keys, bake, numeric checks, and fixed-frame review |
| Retarget between two known skeletons | Chain mapping, retarget pose, root behavior, proportions, and target-side axis discovery |
| Copy an existing animation, switch/use IK, then modify the hand and elbow target | IK/FK scalar handling, endpoint and pole control, and preservation of unedited motion |
| Full-body IK authoring from an existing mesh | Roots, ancestry-valid chains, concrete goals, solver/effector connections, save/reload round trip |
| Bone/socket contact plus a simultaneous unrelated edit | Generic dense constraint solving, smooth transitions, transactional key readback, post-bake residual QA, and preservation outside the constraint |
| Edge-case pack | Right-side mirroring or negative scale, dense source keys, additive-source rejection/flattening, layered sessions, scalar enums/floats, root motion, loop seams, and short clips |
Each fixture records source/session/output asset paths, binding tag, engine version, selected controls and metadata, probe observations, sampled frames and bones, numeric acceptance checks, and clean screenshots. Human approval is the final visual gate, not a replacement for those records.
Exact-duration endpoint gate
Control Rig edit ranges are end-exclusive: the last authorable frame is
endFrameExclusive - 1. A baked AnimSequence can additionally expose a sample
at its exact duration (sourceFrameCount) even though the animation editor's
last visible authored frame is one frame earlier. The bridge keeps an internal
evaluation support frame so Unreal's exporter cannot sample outside the source
and Control Rig sections; the exclusive end is still not authorable.
Validate the last visible authored frame and exact-duration sample separately.
For a non-looping clip, the endpoint must hold the intended final pose within
the fixture tolerance and must not introduce an adjacent-frame teleport or
rotation jump. For a loop, pass loop=true and require the endpoint to satisfy
the intended seam. Treat a large endpoint discontinuity as a failed bake, not
as an Unreal sampling quirk.