Tactical Action · Architecture

Animation Pipeline

A focused page within the Tactical Action Architecture documentation. The full map and every sibling page live in the Architecture hub.

9sections12 minread1diagram1table

On this page

V4 is one game that has to animate like six. A tactical-FPS operator leaning out of cover, a social-stealth assassin dragging a body, a real-time-tactics specialist holding a squad pose, a Wukong-class action-RPG hero chaining a combo into a transformation morph, an RTS unit cycling work-and-march loops, and a 2D run-and-gun pilot flipping through Paper2D frames — all of them are meant to ride the same modern UE5 animation stack: AnimBlueprints with per-cell sub-states, Motion Matching for traversal-heavy areas, IK retargeting through one IKRig per body archetype, Motion Warping for contact alignment, and the MetaHuman pipeline for principal faces. The organizing idea mirrors V4's GAS layer (./gas-layout.md): share one skeleton and one master AnimBlueprint per body family, then specialize with cell-specific slots and state machines so a costume or a verb is an addition, not a fork.

What actually lives in the repo today is the contract and validation layer for that stack, plus two genuinely runtime helpers. The V4Animation module is small and honest: a set of USTRUCT specs, two data-asset manifests that build and self-validate the required AnimBlueprints / motion-matching datasets / IKRigs / retargeters, a real hit-reaction-and-wake-up chooser table that runs its lookups in C++, and a motion-warping handler that calls straight into Unreal's MotionWarping plugin. Unlike V2's fighting-game animation chapter — which was pure config behind unlinked plugins — V4's module links the real animation plugins and makes a real plugin call, but the heavy authored content (compiled AnimBlueprints, pose-search databases, IKRig assets, montages, MetaHuman heads) is not yet present as binary .uasset. This page is part of the Combat, Character & Input Systems group; the section hub is ../V4_ARCHITECTURE.md.

What ships, honestly#

The V4Animation module is real C++ that builds and is tested, but it is a specification-and-runtime-helpers layer, not the authored animation graph. Five honest qualifications, so the rest of the page reads at face value:

  1. The module links the real animation plugins. V4Animation.Build.cs:9-22 adds AnimGraphRuntime, PoseSearch, Mover, Chooser, and MotionWarping to its public dependencies — alongside Core, CoreUObject, Engine, and V4Core. This is a step beyond V2, whose animation module linked none of them. But linking is not calling: only one of those plugins (MotionWarping) is actually invoked from C++ today.

  2. Two of the four classes are validated config; two are runtime. UV4AnimBlueprintManifest and UV4AnimationPipelineManifest are UDataAssets that describe the required AnimBlueprints and the motion-matching / IKRig / retarget / MetaHuman pipeline and then validate that description. UV4AnimationChooserTable and UV4MotionWarpingHandler do real work at runtime — a data-driven decision and a real plugin call, respectively.

  3. There are zero binary .uasset animation assets. V4/ue/Content holds no .uasset files at all; the animation content directory (V4/ue/Content/V4Animation) instead ships 47 *.uasset.v4asset.json descriptors across eight subdirectories (AnimationBlueprints, Choosers, IKRig, MetaHuman, Mocap, MotionMatching, Retargeters, Takedowns). These JSON files mirror — and slightly extend — the C++ specs (the ABP_Operator_Master descriptor lists a Takedown slot the C++ default does not), but no skeleton, AnimBlueprint, pose-search database, IKRig, or montage binary exists for the soft names to resolve to.

  4. The animation classes are not yet wired into gameplay. Outside the module itself, the only consumer of UV4AnimationChooserTable, UV4MotionWarpingHandler, UV4AnimationPipelineManifest, and UV4AnimBlueprintManifest is the automation spec (V4Tests/Private/V4AnimationTests/AnimBPCompileSpec.cpp). No ability, pawn, or combat component instantiates them in the live path yet. They are shippable units awaiting an integration call site.

  5. There is no AnimNotifyState bridge, and no montages. V2 turned montage timelines into combat data through real UAnimNotifyState subclasses (hitbox / armor / cancel-window). V4 has none of those in V4Animation, and no montage .uasset anywhere. The frame-accurate combat windows the GAS verbs depend on — dodge i-frames, the parry window — are computed in combat C++ components in V4ActionRPG, not driven by animation notifies (see How the pipeline connects).

Everything below is real where it cites a function and a line; where it names an authored asset or a plugin runtime, that is the specified-but-pending part.

The module surface#

V4Animation is four public classes over V4AnimationTypes.h, which defines the shared enums and structs. The enums are the vocabulary the rest of the module keys on: EV4MotionWarpContext (Breach / Throw / Finisher, V4AnimationTypes.h:8), EV4HitReactionType (Light / Heavy / Knockdown / Death, :16), and EV4WakeupType (BackRise / RollLeft / RollRight / KipUp, :25). The cell identity itself is shared from core — EV4RulesetCell (V4Core/Public/V4CellState.h:7: None, Tactical, Stealth, Tactics, ActionRPG, RTS, Arcade) — so the motion-matching datasets can be keyed by the same join key the GAS layer uses.

flowchart TD GAS["GAS verb<br/>(UGA_* picks a montage/state)"] --> ABP INPUT["EnhancedInput + FMotionParser"] --> ABP ABP["Master AnimBlueprint per body archetype<br/>state machines + montage slots"] ABP --> SRC{Locomotion source?} SRC -->|traversal-heavy cell| MM["Motion Matching dataset<br/>pose history 8, trajectory 600 ms"] SRC -->|FPS / RTS / standard| RM["Root-motion AnimBP graph"] MM --> POSE["Local pose"] RM --> POSE POSE --> IK["IKRig retarget<br/>(one rig per archetype)"] IK --> FINAL["Final pose to mesh"] GAS -. hit / wake-up .-> CHOOSE["UV4AnimationChooserTable<br/>ChooseHitReaction / ChooseWakeup"] CHOOSE -.-> ABP GAS -. breach / throw / finisher .-> WARP["UV4MotionWarpingHandler<br/>AddOrUpdateWarpTargetFromTransform"] WARP -.-> FINAL

The dashed edges are the two runtime helpers; the solid path (AnimBP → source → retarget → mesh) is the authored graph that today exists as JSON descriptors and validated specs rather than compiled assets.

AnimBlueprint layout — masters, slots, and state machines#

The AnimBlueprint design is captured by FV4AnimBlueprintSpec (V4AnimationTypes.h:94): an AssetName, a list of RequiredSlots (montage slot names), a list of RequiredStateMachines, and a bUsesFlipbooks flag for the 2D exception. UV4AnimBlueprintManifest::LoadDefaultSpecs (V4AnimBlueprintManifest.cpp:32) builds ten master AnimBlueprints — more than the architecture monolith's six, because it also covers hostiles and vehicles:

AnimBlueprint Required slots State machines
ABP_Operator_Master Cover, ADS, Crouch, Prone, BodyDrag Locomotion, Weapon, Injured
ABP_RTSUnit_Master FactionOverride UnitLocomotion, Work
ABP_RTST_Specialist_Master SquadPose, CommandPose SpecialistLocomotion, TacticalAction
ABP_ARPGHero_Master Combo, Parry, Dodge, Transformation CombatCombo, TransformationMorph
ABP_NPC_Civilian Panic, DropAndCover Idle, PanicRun
ABP_2DOperator Flipbook, Weapon ContraCellFlipbook (flipbooks)
ABP_NPC_Hostile_FPS Combat, Suppression, Vault HostileFPSLocomotion, WeaponResponse
ABP_NPC_Hostile_RTST Patrol, Alert, ConeAim RTSTPatrol, HostileTacticalAction
ABP_Vehicle_Driver VehicleSeat, Steering, ExitVehicle DriverPose, VehicleTransition
ABP_Vehicle_Gunner DoorGunner, AimOffset, Reload GunnerPose, MountedWeapon

The pattern is the cell-sharing thesis made concrete: each entry is a master graph for one body/role family, and the cell verbs hang off it as named montage slots (cover-lean, ADS, body-drag, the ARPG combo/parry/dodge/morph) layered over a small set of state machines (always a locomotion machine plus one or two role machines). ABP_2DOperator is the deliberate outlier — its sole state machine is ContraCellFlipbook and it is the only spec with bUsesFlipbooks = true, encoding that the Contra cell is Paper2D rather than skeletal.

ValidateRequiredSpecs (:78) is a genuine self-check, not a tautology: it constructs a fresh required manifest, then for every required spec confirms the actual manifest contains it, contains all of its required slots and state machines (ContainsAllNames, :5), and matches the flipbook flag exactly. A manifest that dropped the BodyDrag slot or flipped a flipbook flag fails. The automation spec asserts the count is 10 and spot-checks that ABP_Operator_Master carries BodyDrag and ABP_Vehicle_Gunner carries DoorGunner (AnimBPCompileSpec.cpp:27-51).

Blending — motion matching, root motion, and shared skeletons#

UV4AnimationPipelineManifest (V4AnimationPipelineManifest.h:9) is the larger data asset, aggregating the motion-matching datasets, IKRigs, retargeters, and MetaHuman pipeline that produce a blended pose. LoadDefaultPipeline (V4AnimationPipelineManifest.cpp:86) populates them with domain-tuned defaults.

Motion matching. Six FV4MotionMatchingDatasetSpec (V4AnimationTypes.h:112) datasets, one per playable cell, each carrying the architecture's promised parameters: PoseHistoryFrames = 8, TrajectoryMilliseconds = 600, and four distance-field cost weightsStanceCost, VelocityCost, AccelerationCost, FacingCost — plus the BakedArchetypes the dataset is trained for. The defaults are not uniform: the Stealth Hitman-sandbox dataset raises StanceCost to 1.25 and FacingCost to 1.0 (stealth cares about stance and which way you face), while the Wukong open-area ARPG dataset raises VelocityCost to 1.15 (:93-120). The intent matches the monolith: motion matching is reserved for traversal-heavy cells (Hitman sandbox, Wukong open areas); FPS and RTS use the standard root-motion AnimBP path, which is why the diagram branches on locomotion source.

IK retargeting and shared skeletons. Five FV4IKRigSpec (V4AnimationTypes.h:145) entries cover the body archetypes — Adult Male, Adult Female, Adolescent, Animal Quadruped, Animal Biped — each declaring its IK chains. Bipeds share a six-chain set (Root, Spine, LeftArm, RightArm, LeftLeg, RightLeg, V4BipedChains() at :8); the quadruped swaps in four legs (V4QuadrupedChains() at :20). Two FV4RetargeterSpec (:160) entries retarget MetaHuman → Operator and Mixamo → Operator, both onto the shared six biped chains. This is the one-skeleton-per-archetype rule: there is no per-operator skeleton, costume and cosmetic differences ride on the shared rig, and animation authored against MetaHuman or Mixamo is retargeted in.

MetaHuman pipeline. FV4MetaHumanPipelineSpec (V4AnimationTypes.h:178) is the most config-shaped piece — five fields, four booleans plus a LipSyncProvider name. LoadDefaultPipeline sets all four configured and names the provider OVRMetaSounds (:133-137). These are capability flags (Creator workflow, Animator facial mocap, Live Link Face, lip-sync runtime) declaring intent, not a wired runtime — there is no Live Link or MetaHuman runtime call in this module.

ValidatePipeline (:140) is the substantive part. For each of the six required cells it finds the dataset, then asserts PoseHistoryFrames == 8, the trajectory is within tolerance of 600 ms, every distance-field cost is strictly positive, and at least one baked archetype is present. For each archetype it confirms an IKRig exists and contains every required chain; for each retarget source it confirms a → Operator retargeter with the full biped chain set; and it fails if any MetaHuman flag is unset or the provider is None. The automation spec drives this end-to-end and pins the headline numbers — 6 datasets, 5 IKRigs, 2 retargeters, the Wukong dataset baked for Hero at depth 8 and 600 ms, and the OVRMetaSounds provider (AnimBPCompileSpec.cpp:60-88).

The runtime decision: hit-reaction and wake-up choosers#

UV4AnimationChooserTable (V4AnimationChooserTable.h:9) is where the module stops describing and starts deciding. It models UE5's Chooser-table concept as a small data-driven matcher with two real lookup functions.

LoadDefaultRows (V4AnimationChooserTable.cpp:3) seeds two row tables. Hit-reaction rows are {MinDamage, MaxDamage, bRequiresHeadshot, Reaction}: a headshot row (0–999, headshot-only → Death) first, then 0–20 → Light, 20–60 → Heavy, 60–99 → Knockdown. Wake-up rows are {MinDownTime, MaxDownTime, bRequiresThreatNearby, Wakeup}: 0–2 s → BackRise, a threat-only 2–5 s → RollLeft, 2–5 s → RollRight, 5 s+ → KipUp.

ChooseHitReaction(Damage, bHeadshot) (:18) walks rows in order, skipping any row whose bRequiresHeadshot is unmet, and returns the first whose half-open damage band contains the hit; it falls back to Light if nothing matches. Because the headshot row is first and spans the whole range, a 5-damage headshot returns Death, while a 35-damage body shot skips that row and lands in Heavy. ChooseWakeup(DownTime, bThreatNearby) (:36) is the same shape gated on threat: 3 seconds down with a nearby threat returns RollLeft; the same 3 seconds without a threat skips that row and returns RollRight. The automation spec asserts exactly those four outcomes (AnimBPCompileSpec.cpp:98-101) — and they would fail against a hardcoded return, because two of them differ only by the boolean flag. This is the genuine selection logic an AnimBlueprint's hit-reaction and get-up state machines would call into; today the call site is the test.

Motion warping — the one real plugin call#

UV4MotionWarpingHandler (V4MotionWarpingHandler.h:11) is a BlueprintSpawnableComponent and the only place V4 reaches into an animation plugin runtime. FV4MotionWarpAlignment (V4AnimationTypes.h:34) describes a warp request: a context (Breach / Throw / Finisher), a TargetName, a target FTransform, translation and rotation offsets, and a bMatchRotation flag.

ApplyWarpAlignment (V4MotionWarpingHandler.cpp:11) stores the request, lazily finds-or-creates a real UMotionWarpingComponent on the owning actor (GetOrCreateMotionWarpingComponent, :49FindComponentByClass, else NewObject + AddInstanceComponent + RegisterComponent), and then calls the engine plugin API UMotionWarpingComponent::AddOrUpdateWarpTargetFromTransform (MotionWarpingComponent.h:155 in the UE5.5 MotionWarping plugin) with the computed transform. That is a genuine integration: a breach, a throw, or a finisher can register a warp target the engine's motion-warping anim node consumes, so root motion bends to align the operator's hands to a door or the hero's strike to a boss.

The transform math is in CalculateAlignedTransform (:27) and is independently testable without an owner: it starts from the alignment's target transform, adds the translation offset, and either concatenates the rotation offset (when bMatchRotation) or preserves the source rotation. The automation spec exercises exactly this — a breach target at (100,0,0) yaw 90° plus a (10,0,0) offset and a 90° rotation offset yields location X 110.0 and yaw 180.0 (AnimBPCompileSpec.cpp:110-118) — concrete arithmetic that pins the offset and rotation-compose behavior.

How cells share and specialize#

The animation pipeline is the same shared-spine / narrow-specialization split as the GAS layer:

  • Shared: one master AnimBlueprint per body/role family (not per character), one IKRig per body archetype with a fixed chain set, the MetaHuman → Operator and Mixamo → Operator retargeters onto a single Operator target skeleton, the 8-frame / 600 ms motion-matching contract, and the two runtime helpers (chooser table, warp handler) that are cell-agnostic.
  • Specialized per cell: which montage slots and state machines the master graph exposes (cover/ADS for Tactical, body-drag for Stealth, combo/parry/ dodge/morph for ARPG, faction-override for RTS, flipbooks for Arcade), and which motion-matching dataset (with its own cost weights and baked archetypes) a traversal-heavy cell loads. Adding a seventh cell means a new master spec, its slots/state-machines, optionally a motion-matching dataset, and a ValidatePipeline arm — with no change to the shared skeletons or helpers.

Because there is exactly one skeleton per archetype and costumes ride on top, a new outfit or a new operator is an asset addition, never a rig fork — the same "mutually exclusive at run-time, shared by construction" property the GAS layer enforces for attribute sets.

How the pipeline connects (and where it does not yet)#

Animation sits downstream of GAS and input. A GAS verb (./gas-layout.md) decides what a character is doing; the AnimBlueprint expresses it as a pose. Input feeds the same chain: EnhancedInput IMCs and the reused V4Input::FMotionParser (a 16-frame directional recognizer, V4Input/Public/V4MotionParser.h:10) drive the ARPG combo verbs that the ABP_ARPGHero_Master Combo/Parry/Dodge slots animate — detailed in ./input-pipeline.md.

The honest seam is the frame windows. The gas-layout page notes abilities "drive the dodge/parry/posture frame windows"; those windows are computed today in combat components — UV4DodgeComponent (InvincibilityFrames = 9, IsInvincibleAtFrame) and UV4ParryComponent (ParryWindowFrames = 6, IsFrameInsideParryWindow) in V4ActionRPGnot by animation notify states, of which V4Animation ships none. So the integer-frame combat truth and the visual animation are presently parallel tracks: the C++ windows are real and tested, and the montages that would visualize them are not authored. Wiring the chooser table and warp handler into those verbs, and authoring the AnimBlueprint and montage .uasset the specs describe, is the remaining integration work.

The cloth, hair, facial-performance, and lip-sync presentation of these poses — the MetaHuman runtime side the FV4MetaHumanPipelineSpec flags stand in for — belongs to the presentation layer, not this module; see ./presentation-pipelines.md.

  • GAS Layout — the verbs that pick montages and the combat-side dodge/parry/posture frame windows animation visualizes
  • Input Pipeline — EnhancedInput IMCs and the FMotionParser that feed the ARPG combo/parry/dodge animation slots
  • Presentation Pipelines — cloth, hair, facial performance, and lip-sync that dress the poses this module produces
  • The section hub: ../V4_ARCHITECTURE.md