Overview

Shape property animations are per-layer, additive modifications applied by the ShapePropertyAnimator (SPA) at runtime. Rather than baking motion into the shader, RCE evaluates animation waveforms on the CPU each frame and writes the resulting deltas directly into the ShapeData buffer before it reaches the GPU. This keeps the shader simple and allows any combination of animated properties per layer.

The animation system sits in the middle of the rendering pipeline:

LoadoutManager
CategoryTransitionAnimator
ShapePropertyAnimator
LoadoutRendererBridge
GPU

Animated values are additive — the animator computes a delta and adds it to the base shape data each frame. The one exception is scale, which uses multiplicative blending: scale *= 1 + delta. This means a delta of 0 leaves the shape unchanged, while a delta of 1.0 doubles the scale.

All animation timers reset when RebuildAnimationMapping() fires, which happens on every category switch. This ensures ramp-in envelopes and tween entries always play from the beginning when the player transitions between combat states.

Edit vs. Play

Animations are off by default in edit mode. Use the Preview toggle in the Animations Panel to enable them while designing. In test mode and at runtime, animations are always active.


Animation Modes

Every animation entry specifies one of seven modes. The first six are continuous waveforms; the seventh (Tween) is a one-shot interpolation. Each mode's formula determines how the delta evolves over time.

# Mode Formula Typical Use
0 Spin speed × Σ(dt × envelope) Continuous rotation using envelope-weighted accumulation — ramp in/out modulates rotation rate (angular velocity) instead of scaling the accumulated angle, preventing visual reversal
1 Oscillate amplitude × sin(2π × freq × t) Smooth back-and-forth motion, breathing effects
2 Pulse amplitude × pow(abs(sin(π × f × t)), sharpness) Heartbeat, flash effects — sharpness controls the spike width
3 PingPong amplitude × asin(sin(…)) / π / 2 Triangle wave — linear back-and-forth with sharp reversals
4 Sawtooth amplitude × frac(freq × t) Ramp-and-snap — linear ramp up then instant reset
5 Noise amplitude × PerlinNoise(seed, t × freq) Organic jitter, screen shake, battlefield vibration
6 Tween fromDelta × (1 − eased(t / dur)) One-shot intro/exit — interpolate from an offset to the base value

Waveform modes (0–5) run continuously and are shaped by speed, amplitude, frequency, phase, and sharpness parameters. Tween mode (6) has its own dedicated parameter set — see the Tween Mode section below.


Animatable Properties

Each animation entry targets exactly one property from the AnimatableProperty enum. Multiple entries can target the same property on the same layer — their deltas are summed.

Property Delta Type Description
RotationAdditive (degrees)Rotates the shape around its center
PositionXAdditive (UV units)Horizontal offset from base position
PositionYAdditive (UV units)Vertical offset from base position
PositionXYAdditive (UV, both axes)Single entry animates X and Y; uses amplitudeY, speedY, fromDeltaY, phaseY for the Y axis. Set phaseY = 90° for circular motion.
ScaleXMultiplicativeHorizontal scale factor (scale.x *= 1 + delta)
ScaleYMultiplicativeVertical scale factor (scale.y *= 1 + delta)
ScaleUniformMultiplicativeUniform scale (scale *= 1 + delta)
ColorAlphaAdditive (0–1)Shape fill opacity
GlowIntensityAdditiveGlow brightness multiplier
GlowSizeAdditiveGlow spread radius
FeatherAdditiveEdge anti-aliasing softness
GapRotationAdditive (degrees)Rotation of the gap pattern
GapThicknessAdditiveGap line or radial thickness
OutlineAlphaAdditive (0–1)True outline opacity
TaperAmountAdditive (0–1)LineBurst taper end sharpness
ColorLerp (0→1)Full RGBA lerp between colorA and colorB; the animation delta drives the lerp factor. Overrides the shape's base color. Typically used with Tween mode.

PositionXY

When the target property is PositionXY, a single entry animates both axes. The primary parameters (amplitude, speed, fromDelta, phase) control the X component; the secondary parameters amplitudeY, speedY, fromDeltaY, and phaseY control the Y component. Set phaseY = 90° with matching amplitude/frequency for X and Y to get circular or elliptical motion.

Color

The Color property animates the shape's fill color by lerping between two RGBA colors (colorA and colorB). The waveform or tween output is interpreted as a lerp factor: 0 = full colorA, 1 = full colorB. Color entries default to Tween mode with colorA initialized from the layer's current color. The editor provides From/To color swatches and pickers when Color is selected.


Common Parameters

Several parameters are shared across all or most animation modes.

Delay

Every animation entry — waveform or tween — supports a delay (in seconds) before the animation begins. During the delay period, tween entries hold at their starting offset (for "In" direction) or at zero (for "Out" direction). Waveform modes output zero delta during the delay.

Ramp In / Out

Waveform modes (Spin, Oscillate, Pulse, PingPong, Sawtooth, Noise) support envelope shaping via ramp in and ramp out durations. The ramp acts as a multiplier on the waveform output, fading it from 0 → 1 at the start or 1 → 0 at the end. The ramp easing parameter controls the curve shape (any EasingType value). Tween mode does not use ramp — it has its own easing via tweenEasing.

Loop Modes

ModeBehavior
LoopRepeat continuously. Spin always loops regardless of this setting.
OncePlay one full cycle then hold at the end value.
PingPongPlay forward then reverse, smoothly alternating. For Tween: offset → base → offset. For waveforms: behaves like Loop.

Unique Entry IDs

Each ShapeAnimationEntry is assigned a GUID id field at creation time. This ID is used for stable timer state keying (so entries retain their elapsed time across rebuilds within the same session) and for UI tracking in the Animations Panel. Duplicate properties are allowed — you can have two Rotation entries on the same layer, each with different modes or phases, and their deltas will sum.

Parameter Randomization

Entries can randomize numeric parameters within a min/max range so each run (or each cycle) varies. Toggles: randomizeSpeed, randomizeAmplitude, randomizeFrequency, randomizeFromDelta. When enabled, the corresponding max field defines the upper bound; the main field is the lower bound (values are clamped so min/max order doesn't matter). Randomized values are resolved once when the entry becomes active (at RebuildAnimationMapping). When rerollEachCycle is enabled, values are re-resolved at each loop or cycle boundary (e.g. each Spin period, each Tween duration, or each waveform period). PositionXY entries can randomize both axes via the same toggles (amplitudeY/fromDeltaY/speedY use the same randomization as the primary parameter).


Tween Mode

Tween is a one-shot interpolation from a saved starting offset back to the base value (or the reverse). It is the primary tool for intro and exit animations — shapes that scale up from nothing, fade in from transparent, or slide into position when a combat state activates.

ParameterDescription
fromDeltaThe starting offset from the base value. For scale: fromDelta = 1.0 means start at 2× the base scale.
durationTotal interpolation time in seconds.
tweenEasingEasing curve for the interpolation (EasingType). Separate from the waveform rampEasing.
reverseDirectionfalse = "In" (offset → base). true = "Out" (base → offset).

UI Display Conventions

The editor adapts the "From" field label and value display based on the target property:

Helper methods DisplayValueToFromDelta(), FromDeltaToDisplayValue(), and GetTweenFromLabel() handle conversions between the UI-facing values and the stored internal representation.

Example: Scale-in Tween

To make a shape grow from zero to its base size when a category activates, add a Tween entry targeting ScaleUniform with direction In, fromDelta display of 0.0× (stored as -1.0), duration 0.3s, and easing EaseOutBack for a satisfying overshoot.


Animation Sequences

By default, all animation entries on a layer run simultaneously and independently. The Animation Sequence system provides optional sequential execution — entries play one after another in the order they appear in the list.

Enabling Sequences

Toggle Sequence Mode on the layer to switch from independent to sequenced playback. When enabled, you mark a contiguous range of entries with Start and End markers using the AnimationSequenceRole enum. Entries between the markers are automatically part of the sequence; entries outside the markers continue to run independently.

Sequence Settings

These settings live on LayerData and apply to the layer's entire sequence:

SettingDescription
animationSequenceEnabledMaster toggle for sequence mode on this layer.
sequenceLoopWhether the sequence repeats after the last entry completes.
sequenceLoopCountNumber of loops. -1 = infinite.
sequencePingPongWhen looping, alternate direction (forward → reverse → forward).

Duration in Sequences

When a waveform entry runs inside a sequence, the duration field controls how long it plays before the sequence advances to the next entry. A value of 0 uses the natural duration for "Once" loop mode. A value of -1 holds indefinitely for "Loop" or "Spin" entries (useful for a sustained state before an external trigger advances the sequence).

Cumulative Mode

When cumulative is enabled on an entry, its delta stacks on top of the accumulated delta from all prior entries in the sequence that target the same property. This enables seamless chaining — for example, a Tween that rotates right 45° followed by a Tween that rotates left 30° from the current position, rather than snapping back to base before each entry.

Hold on Complete

When an entry inside a sequence has Hold on Complete (holdOnComplete) enabled, its final delta is kept and applied every frame for the rest of the sequence instead of resetting when the next entry starts. This is useful for building up state step-by-step (e.g. rotate 30°, then hold that rotation while the next entry animates position). The toggle appears in the Animations Panel for entries within a sequence range.

Drag-and-Drop Reordering

Entries can be reordered via drag handle in the Animations Panel. Start/End markers are validated after every reorder — if a Start marker ends up after an End marker, both markers are automatically cleared to prevent invalid state.

Sequence Events

The OnSequenceEvent callback fires AnimationSequenceEventArgs at key points in the sequence lifecycle:

EventWhen It Fires
SequenceStartedThe sequence begins playback (or restarts after a loop).
EntryStartedAn individual entry within the sequence becomes active.
EntryCompletedAn individual entry finishes its duration.
SequenceLoopedThe sequence completes one full pass and loops.
SequenceEndedThe sequence finishes all loops (or hits the end in non-looping mode).

Blend Modes

Each animation entry has a blend mode (AnimEntryBlendMode) that controls how its delta interacts with other entries targeting the same property on the same shape.

ModeBehavior
Additive (default) Delta stacks with other entries' deltas. Multiple Additive entries on the same property are summed.
Override When active, temporarily suppresses all Additive entries for the same property on this shape. Uses rampIn for smooth takeover crossfade and rampOut for smooth release back to the base state. Additive entry timers keep advancing while suppressed, so they resume at the correct phase when the override releases.

Override Blend Lifecycle

An Override entry goes through a well-defined lifecycle:

  1. Ramp In — The override's blend weight fades from 0 → 1 over rampIn seconds. During this phase, Additive entries are scaled down by (1 - overrideWeight) and the override's own output is scaled up by overrideWeight. Smoothstep interpolation eliminates jerk at ramp boundaries.
  2. Active — The override has full control (weight = 1.0). Additive entries are fully suppressed (except Spin, which uses rate-based suppression).
  3. Completion — When the override's waveform or tween finishes (Once loop mode, or a Spin with explicit duration), the final delta is frozen.
  4. Ramp Out (Release) — The frozen delta blends from 1 → 0 over rampOut seconds, smoothly returning control to Additive entries.
  5. Fully Released — The override has no effect. For awaitTrigger entries, it automatically re-arms for the next trigger.
Spin Override

Spin (continuous rotation) overrides use rate-based crossfading rather than position-based. The override modulates the rotation speed instead of scaling the accumulated angle. This avoids visual reversal (the shape briefly unwinding). During late release, the override automatically snaps to the nearest 360° boundary so the handoff to Additive entries is imperceptible.

Override Tween + Ramp In

When an Override entry uses Tween mode and has a non-zero rampIn, the tween holds at its starting value during the ramp-in phase. This prevents a non-monotonic peak where the decreasing tween delta fights the increasing blend weight. The tween interpolation begins only after the ramp-in completes.

Typical Use Case: Reactive Weapon Wobble

A common pattern is a continuous Additive Spin on rotation (base animation) paired with an Override Oscillate on the same property, set to awaitTrigger. When the player fires, the trigger starts a brief wobble that overrides the spin, then smoothly releases back to the continuous rotation.


Await Trigger

The awaitTrigger flag (ShapeAnimationEntry.awaitTrigger) makes an animation entry stay dormant until explicitly triggered at runtime. While dormant, the entry's timer is frozen and it produces no output.

Trigger Mechanics

Auto-Play with Trigger

When a GlobalFX layer has autoPlay enabled, calling TriggerLayerByName() on an idle layer will automatically start it using the layer's autoPlayMode before firing the trigger. This means you can design a Global layer with a continuous base animation (e.g. idle pulse) and trigger-reactive overlays (e.g. fire wobble) — calling TriggerLayerByName() handles the entire lifecycle.

Ideal For

Weapon-fire recoil wobble, hit confirm flashes, ability activation bursts — any brief reactive effect that should overlay a continuous base animation and automatically return to normal afterward.


Apply to Operands

When a shape is the base layer of a boolean group, its transform animation entries (Rotation, PositionX, PositionY, PositionXY, ScaleX, ScaleY, ScaleUniform) gain an Apply to Operands toggle (applyToOperands). When enabled, the animated delta computed for the base shape is also written to every consecutive operand shape in the GPU array at runtime.

This lets the entire boolean group move, rotate, or scale as a unit without requiring duplicate animation entries on each operand. The toggle appears per-entry in the Animations Panel and is only visible for transform properties on base layers of groups.

Note

Apply to Operands is a runtime GPU-level operation — it writes deltas to the operand slots in the shape buffer. It is independent of the Link Operands setting on the layer, which controls editor-time parent/child transform propagation.

Pivot (Orbital Rotation)

When a layer has Use Pivot enabled (layer.usePivot), rotation animations orbit the shape around the layer's pivot point in UV space instead of rotating around the shape's own center. This is useful for elements that should spin around a fixed point (e.g. a hand on a clock). For boolean groups, when a rotation entry has Apply to Operands enabled, operand shapes orbit around the base shape's position (or pivot), so the whole group rotates as a rigid body around that point.


Transition Interaction

The animation system is designed to cooperate cleanly with Category Transitions. During an active transition, ShapePropertyAnimator pauses — it skips rebuild while the CategoryTransitionAnimator (CTA) is interpolating between states.

Post-Transition Pop Prevention

When a transition finishes (postTransition = true), tween-in entries for certain "pop-prone" properties are force-completed — their elapsed time is set past their duration so they resolve immediately to their end value. This prevents a visual pop where a shape would snap from its base position back to a tween start offset. The force-completed properties are:

Non-pop properties like Rotation and PositionX/Y play their tween-in animation normally after the transition ends, providing a natural entrance effect.

CTA Animation Exclusion

The CategoryTransitionAnimator calls BuildTransitionAnimations to construct intermediate frames during a transition. It excludes all non-passthrough animations (not just tweens) from these transition frames, so that category animations start fresh in the ShapePropertyAnimator once the transition completes. The _wasInTransition flag in SPA tracks transition state for postTransition detection.


GlobalFX Integration

Multiple ShapePropertyAnimator instances can coexist in the same scene. The main reticle pipeline has one SPA, the Global FX pipeline has a second (configured via ConfigureAsGlobalFX), and the RenderTexture output pipeline creates a third (configured via ConfigureForRT). Each SPA independently tracks its own entry timers, sequence state, and override blend weights.

GlobalFXBridge routes TriggerLayer() calls to the correct SPA using HasLayerInShapes(layerId) — if a layer's shapes are being rendered by the RT manager's SPA, triggers are routed there; otherwise, they go to the main GlobalFX SPA.

Paused layers remain visible but their timers do not advance; stopped layers return to base state. See GlobalFX API for PauseLayer, StopLayer, TriggerLayer, and play modes.


Animations Panel UI

The Animations Panel is a dedicated floating popup opened via the "Edit Animations" button in the layer properties panel. Each layer gets its own panel instance.

Layout

Entries are displayed in an accordion view — each animation is a collapsible card tracked by its unique entry ID. The panel shows:

Sequence Settings Section

At the top of the panel, a collapsible section provides: Sequence Mode toggle, Loop toggle, Loop Count field, and Ping-Pong toggle. These control the layer-level sequence configuration.

Draggable Panel

The panel is draggable via its title bar. Position persists in PlayerPrefs between sessions. The panel blocks scene interaction while visible via the IsOverVisiblePanel() bounds check, preventing accidental shape manipulation while editing animations.

Inline Summary

In the layer list, layers with active animations display a ~ badge. Additional indicators are appended to the animation summary text:

BadgeMeaning
~Layer has one or more animation entries
+At least one entry has cumulative mode enabled
[H]Entry has Hold on Complete enabled (sequence)
[S]Entry is marked as sequence Start
[E]Entry is marked as sequence End
[OVR]Entry uses Override blend mode
[T]Entry has awaitTrigger enabled

What's Next