Procedural Animator

Engine-agnostic procedural motion that composites additive PoseDelta contributions from weighted layers, lifecycle-driven actions and one-shot activation sequences — across five declared channels — onto any rig, prop or UI element.

What the system is for

Skeletal animation and procedural motion are not opposites — they work best together. A character can play a locomotion clip and breathe, sway and flinch at the same time, because all those contributions are additive offsets layered on top of whatever the animator authored. The difficulty is doing this cleanly, without the procedural code knowing anything about Unity transforms, renderers or the update loop.

Serenity's Procedural Animator system handles that. It defines a compositing model built around PoseDelta, separates the evaluation math from the transform application through IRigApplier, and drives the whole pipeline from a ScriptableObject profile so the setup lives in assets, not in code.

The Unity problem

Most Unity projects bolt procedural motion on late: a MonoBehaviour that directly rotates a spine bone for breathing, another that lerps a head transform for look-at, and a coroutine that snaps the camera for recoil. Each one reads from Unity transforms, writes back to Unity transforms, and steps on every other script that also touches those transforms. The result is order-of-operations bugs, animation fights and a system nobody wants to extend.

When a hit reaction, a breathing layer and an aim offset all write to the same bones independently, the final pose depends on script execution order and which Update ran last. There is no compositing budget, no weight system and no way to preview the combined result without entering Play Mode.

How Serenity approaches it

Procedural Animator introduces two participation contracts. IProceduralLayer<TContext> is for continuous contributions — breathing, sway, look-at — that evaluate every frame and write into a PoseDelta weighted by the layer's current weight. IProceduralAction<TContext> is for discrete lifecycle-driven one-shots — recoil, flinch, stumble — that implement CanStart, OnStart, Tick, IsFinished and OnStop. Both write additive offsets into the same PoseDelta accumulator so their contributions compose correctly. Every layer also declares which of five channels it actually writes — Pose, Material, Transform, Activation and UiColor — through a DrivenChannels flag, so a consumer applies only the channels it cares about instead of guessing from the layer's concrete type.

IRigApplier<TRigDefinition> isolates the actual transform manipulation from the evaluation math. The compositing pipeline accumulates a PoseDelta, a MaterialDelta, a TransformDelta and an ActivationDelta, then hands them to the applier. TransformRigApplier and UnityMaterialApplier provide the Unity implementations, but the domain logic never touches a Unity type. RuleSO connects conditions to layer weights or action triggers so behavior is configured in assets through ProceduralAnimatorProfileSO.

How it fits into Serenity

Procedural Animator lives in the Serenity.ProceduralAnimator namespace and follows the foundation's layered structure. The Domain layer defines PoseDelta, MaterialDelta, TransformDelta, ActivationDelta, BoneId, MaterialId and IStateReader — all engine-agnostic. The Application layer declares IProceduralLayer<TContext>, IProceduralAction<TContext> and IRigApplier<TRigDefinition>. The Infrastructure layer provides ProceduralLayerSO and ProceduralActionSO base classes, concrete layers and actions, weight providers, condition types, applier implementations, editor tooling and ProceduralAnimatorComponent as the runtime driver.

ProceduralAnimatorProfileSO is the composition root: it groups BaseLayers, Rules, MaterialTargets and TransformTargets into a single asset. Rules link Condition trees to either a SetLayerWeight or TriggerAction effect so the active pose is entirely data-driven. WeightProviderSO implementations — ConstantWeightProviderSO and StateKeyWeightProviderSO — let layer weights respond to runtime state values on StateBusComponent. Beyond continuous rig motion, a layer can run in ON_ACTIVATE phase mode — a one-shot sequence with an authored repeat count that restarts on every trigger instead of looping the shared animation clock — and an ActivationSequenceLayerSO can flip a target GameObject on and off as part of that sequence. The system pairs naturally with the input and weapon systems for recoil and rumble, and with the sequence system for cutscene-driven expression changes.

Practical workflow

  1. Add ProceduralAnimatorComponent and UnityRigDefinition to the character prefab and bind bone transforms by name — or set drivesRig to false for a non-rig animator that drives only materials, transforms or activation targets on its own manual clock, such as a UI element or a prop.
  2. Create a ProceduralAnimatorProfileSO asset and assign continuous layers such as PeriodicOscillationLayerSO for breathing or PerlinDriftLayerSO for sway.
  3. Create RuleSO assets that link Condition trees to layer weight targets or action triggers; use ThresholdCondition or RangeCondition against state key values.
  4. Create ProceduralActionSO assets for discrete reactions — StunRecoilActionSO, FlinchActionSO or StumbleActionSO — and reference them from rules.
  5. Use StateBusComponent as the runtime state store; push values from gameplay code using UnityStateKey assets so layers and conditions react automatically.
  6. Use the ProceduralAnimatorSetupWindow wizard (Tools ▸ Serenity ▸ Create ▸ Procedural Animator ▸ Setup Minimal Idle) to generate a scaffold with StateKeys, layers, weight providers and a minimal idle profile in one step.

What you get

  • IProceduralLayer<TContext> contract for continuous weighted contributions (breathing, sway, look-at, noise)
  • IProceduralAction<TContext> contract for discrete lifecycle-driven one-shots (recoil, flinch, stumble)
  • PoseDelta, MaterialDelta, TransformDelta and ActivationDelta accumulators composited before any transform is touched
  • IRigApplier<TRigDefinition> isolates bone application from evaluation math for full engine portability
  • ProceduralAnimatorProfileSO as the data-driven composition root grouping layers, rules and targets
  • Every layer declares its DrivenChannels (Pose, Material, Transform, Activation, UiColor) so a consumer applies only the channels it actually writes
  • ON_ACTIVATE phase mode with an authored repeat count for one-shot, retriggerable sequences, plus a GameObject on/off activation sequence layer
  • Non-rig animators run on a manual clock for UI elements and props — the same pipeline drives the HUD Builder's animation presets and the Animation Hub's live procedural simulator (play/pause/step, per-state-key sliders)
  • RuleSO connects Condition trees to layer weight changes or action triggers without code
  • WeightProviderSO implementations including state-key-driven curve mapping for reactive layer weights
  • StateBusComponent runtime state bus with optional smoothing for decoupled state publication
  • Built-in concrete layers: PeriodicOscillationLayerSO, PerlinDriftLayerSO, HighFrequencyNoiseLayerSO, StateModulatedOffsetLayerSO, RandomImpulseLayerSO, ActivationSequenceLayerSO
  • Built-in concrete actions: StunRecoilActionSO, FlinchActionSO, StumbleActionSO with curve-driven attack and recovery

When to use this

  • Characters that need breathing, idle sway or look-at running alongside clip-based locomotion without animation fights.
  • Weapon systems that need recoil, hit flinch or stagger reactions as discrete additive contributions on top of the base animation.
  • HUD elements and other non-rig UI or props that need the same layered, data-driven motion as a character rig — pulses, blinks and one-shot activation sequences with zero extra code.
  • Projects that want procedural motion configured in assets rather than hardcoded in MonoBehaviours spread across scenes.
  • Codebases that need the same compositing logic to work regardless of how bones or transforms are applied in the target rig.

Related systems

Use Serenity when you want procedural motion that composes cleanly with skeletal animation — or drives UI and HUD elements without one — responds to runtime state through data-driven rules, and never touches a Unity transform until the full delta is ready.

Back to the home page