How-To: Author Motion Presets

Motion presets live in a UCrimsonCombatTextMotionSet asset as ordinary editable rows keyed by gameplay tag. Adding your own needs no C++.

One preset, many styles
Define a motion once and point many styles at it. Editing the preset updates every style using it - which is the whole reason motion is a shared asset rather than a per-style struct.

1. Create the asset

Content Browser right-click > Crimson > Combat Text > Motion Set. A new asset arrives pre-seeded with ten editable built-in presets, so you are never staring at an empty map:

Preset tagFeel
CrimsonCombatText.Motion.FloatUpGentle straight rise with a soft ease-out. The everyday damage number.
CrimsonCombatText.Motion.ArcOutRises then falls away in an arc. Classic ballistic combat text.
CrimsonCombatText.Motion.BurstWide fast scatter, short life. Multi-hit and AoE.
CrimsonCombatText.Motion.CritPunchHard scale punch, minimal drift, long hang time.
CrimsonCombatText.Motion.StaticNo movement. Fades in place. Good for status words.
CrimsonCombatText.Motion.DriftSlow, small, long-lived rise with a wide gentle fan. Ticks of a damage-over-time.
CrimsonCombatText.Motion.SinkFalls away instead of rising. Blocked and absorbed damage - anything that failed to land.
CrimsonCombatText.Motion.HoverQuick hop, then a long hang in place. Status words: Miss, Immune, Dodge.
CrimsonCombatText.Motion.ShiverRapid side-to-side wobble on the way up. Ice, lightning, anything that should read as unstable.
CrimsonCombatText.Motion.PulseRises while the number beats twice. Heals and buffs that should feel alive.

Point the registry's MotionSet at this asset, then reference presets from a style's MotionPreset tag.

See them in use
Press Add Examples in the style registry editor and you get authored rows pointing at eight of these ten - including three pairs of styles deliberately sharing one preset, which is the cheapest way to see what a shared motion asset buys you. See How-To: Author Styles in the Registry Editor.
Motion sets open in their own editor
Double-click the asset and you get three panes instead of a nested map: a tree of preset tags, the selected preset's details, and an animated preview that plays the motion you are editing, fan and stagger included. The toolbar adds, duplicates and deletes presets, and Add Built-Ins puts back any shipped preset you removed without touching what you authored. New preset tags come from a tag picker filtered to CrimsonCombatText.Motion. A Merge checkbox in the preview's chrome row plays the merged form instead - held at MergeHoldAlpha, punching on a repeating cycle - so the merge fields are tuned against what they actually look like. See How-To: Author Styles in the Registry Editor.

2. Add your own

Add a gameplay tag under CrimsonCombatText.Motion and a matching row. Tune the amplitudes first, then shape the curves:

Motion Set asset: the Presets map with a preset row expanded, showing RiseCurve open in the curve editor.
FieldWhat to reach for it for
DurationHow long it lives
RiseDistanceHow far it travels up over the full lifetime
SpreadDistanceLateral amplitude at full fan deflection
RiseCurveThe shape of the rise. Empty = linear
SpreadCurveThe shape of the lateral travel. Empty = linear
ScaleCurvePunch and settle. Empty = constant 1
OpacityCurveFade. Empty = opaque until 80%, then linear to 0
RiseStaggerPerSlotExtra rise per concurrent hit, as a fraction of RiseDistance. Ladders the end heights of a burst
RiseJitterRandom rise variation as a fraction of RiseDistance, rolled once per pop
DurationJitterRandom variation in Duration as a fraction of it, rolled once per pop, so pops in a burst climb at different speeds
MergeHoldAlphaWhere the motion parks when a hit merges into this pop, as a lifetime alpha
MergePunchDurationHow long the merge punch runs, in seconds. 0 = hold with no punch
MergePunchScaleCurveThe punch's shape, as a scale multiplier on the held sample. Empty = a sine hump 1 -> 1.3 -> 1
MergePunchRiseCurveExtra upward layout units over the punch, added to the held sample. Empty = 0
Curves are lifetime-relative, not seconds
Curves run over a normalized 0..1 lifetime alpha, not seconds - so changing Duration restretches the whole motion without redrawing anything.

3. Stop hits stacking

bUseSpreadFan deflects concurrent hits on the same target to alternating sides. Slot 0 goes straight up, then -1/N, +1/N, -2/N and so on out to FanWidth. It is deterministic, so the same burst always reads the same way; RandomJitter adds controlled wobble on top if perfect regularity looks mechanical.

The fan only moves hits sideways, and it saturates at FanSlots - so past that slot a burst on one target still ends at a single height and the numbers pile up. RiseStaggerPerSlot ladders the END heights as well: slot 1 travels RiseDistance * (1 + RiseStaggerPerSlot), slot 2 twice that step, and so on up to FanSlots. Negative values ladder downward; 0 restores a flat stack. RiseJitter then adds a per-pop roll on top, so a second volley does not replay the identical ladder. DurationJitter varies each pop's lifetime the same way, because different end heights still read as one clump when every number climbs in lockstep - built-in defaults are FloatUp 0.15, ArcOut 0.20, Burst 0.25, CritPunch 0.10, Static 0, Drift 0.30, Sink 0.15, Hover 0.10, Shiver 0.20, Pulse 0.15.

PresetRiseStaggerPerSlotRiseJitter
FloatUp0.100.06
ArcOut0.080.10
Burst0.150.12
CritPunch0.000.05
Static0.000.00
Drift0.120.10
Sink0.080.06
Hover0.150.05
Shiver0.100.08
Pulse0.100.06
Both scale RiseDistance, they do not add to it
Stagger and jitter are multipliers on RiseDistance, not offsets - so a motion that deliberately does not travel (Static, RiseDistance 0) stays pinned to its anchor however many hits land at once. The multiplier is also clamped at 0, so a large negative stagger cannot flip a pop downward.
Verify
Fire a burst of eight at one target from the Dev Tools Spawn tab. The pops fan sideways AND end at visibly different heights; repeat it and the ladder is the same while each number sits slightly off its previous spot. Switch the style to CrimsonCombatText.Motion.Static and every pop stays on the anchor.

SpreadDecayTime in project settings controls how long a burst keeps fanning outward before the next flurry restarts from center.

4. Shape what a merge looks like

When a style merges repeat hits into one climbing number - AggregationWindow or ScopeTag - the pop does not restart and does not rewind to the anchor. It holds at MergeHoldAlpha and fires a punch on top, and the four fields under the Motion | Merge category are where you shape that.

FieldReach for it to
MergeHoldAlphaMove where the number parks. Lower parks it closer to the anchor and leaves more travel for the fade; higher parks it further up. Default 0.6
MergePunchDurationSet how long each reaction lasts. Shorter reads as a snappier tick; 0 turns the punch off and leaves a plain hold. Default 0.25s
MergePunchScaleCurveShape the size kick. Empty gives a sine hump from 1 to 1.3 and back, which is a sensible default for most numbers
MergePunchRiseCurveAdd a small hop. The values are layout units added to the held offset, so a peak of 8 nudges the number up and back down over the punch

The punch has its own alpha, separate from the lifetime alpha, so back-to-back merges re-punch while the base motion stays parked. The built-in presets are tuned for their own feel: FloatUp holds at 0.55 and punches for 0.22s peaking at 1.30 scale with an 8-unit hop, Drift 0.50 / 0.18s peaking at 1.18 with a 4-unit hop, CritPunch 0.50 / 0.30s peaking at 1.45 with a 14-unit hop, and Hover and Static hold at 0.60 and punch for 0.25s with scale only and no hop, so a pinned status word reacts without moving.

Verify
Tick Merge in the preview's chrome row. The pop climbs to MergeHoldAlpha and stops there, then punches on a cycle - it never snaps back to the anchor and never restarts. Set MergePunchDuration to 0 and the punching stops while the hold stays.

Overriding motion from a modifier

A modifier's MotionPresetOverride swaps the motion wholesale. Pointing a Crit modifier at CrimsonCombatText.Motion.CritPunch gives every critical hit the punch motion, whatever base style it resolved to - no per-damage-type rows needed.