Text Animator
The Text Animator (UTextAnimator) is Texturge’s runtime animation pipeline engine. It runs as a standalone UObject, independent of Actor or ActorComponent lifecycles, held by Slate widgets and driven frame-by-frame via Tick().
Responsibilities
Input Processing Output
──────────────────────────────────────────────────────────────────────
PlainText / RichText → ExecutePipeline() → FAnimationFrameData[]
TextAnimationBlueprint → Compile AnimInstance → DisplayRichText
AnimationDataAsset → Build RenderTree → OnCharacterRevealed delegate
PlayMode / CPS / Speed → AdvanceFrame() → OnAnimationComplete delegate
Two Pipelines
The animator supports two mutually exclusive animation pipelines, automatically selected based on input type:
Plain Text Pipeline
Used by UAnimatedTextBlock, a single animation blueprint drives the entire text:
- Word Breaking — Perform character breakpoint analysis on PlainText (considering CJK surrogate pairs and Unicode boundaries)
- Compilation — Create and compile a
UTextAnimInstancefrom theTextAnimationBlueprint - Frame Generation —
EvaluateTime()samples all tracks at each time point, blending to produce per-character frame data - Timing Calculation — Compute per-character reveal time based on
PlaybackMode(Duration / CPS)
Rich Text Pipeline
Used by UAnimatedRichTextBlock, driven by a render tree for multi-layer animation:
- Parsing —
FTagParserparses rich text into a tag tree - Render Tree Construction —
FRenderTreeBuilderclassifies nodes (AnimationLayer / StyleLayer / TextContent / Decorator) - Per-Layer Compilation — Each AnimationLayer node gets its own
UTextAnimInstance - Per-Layer Frame Generation — Each animation layer’s frame data is evaluated independently
- Timing Calculation — Based on the longest total duration across all layers
Playback Control
Play Modes
| Mode | Enum Value | Behavior |
|---|---|---|
| Normal | Normal | Forward playback, stops at end |
| Fast Forward | FastForward | Accelerated forward playback (rate controlled by PlaySpeedMultiplier) |
| Reverse | Reverse | Reverse character-by-character hiding |
| Ping Pong | PingPong | Forward → reverse → forward cycle |
| Loop | Loop | Restarts from beginning after completion (controlled by MaxLoopCount, ≤0 for infinite) |
Timing Modes
| Mode | Description |
|---|---|
| Duration | Fixed total duration. Animation completes within TotalDuration seconds, with equal time per character. |
| CPS | Characters Per Second. Characters revealed at a fixed rate (CPS, default 10 chars/sec). |
Public API
// Input
void SetPlainText(const FString& InText);
void SetRichText(const FString& InRichText);
void SetAnimationData(UTextAnimationDataAsset* InData);
void SetAnimationBlueprint(UTextAnimationBlueprint* InBlueprint);
// Control
void StartAnimating(bool bRevealAll = false);
void TickAnimation(float InDeltaTime);
void Pause();
void Resume();
void Stop();
void SkipToEnd();
// Query
bool IsAnimating() const;
int32 GetCurrentCharIndex() const;
int32 GetCharacterCount() const;
const FString& GetPlainText() const;
const FString& GetDisplayRichText() const;
const TArray<FAnimationFrameData>& GetCurrentFrameDataArray() const;
FAnimationFrameData GetCurrentFrameData(int32 CharIndex) const;
Frame Data Cache
CurrentFrameDataCache is the animator’s core output, updated every Tick — a TArray<FAnimationFrameData> with length equal to the text character count. This cache is directly consumed by the Slate widget’s OnPaint():
- Revealed characters: evaluated animation frame data in the cache
- Unrevealed characters: identity frame (
Opacity=0) in the cache, producing no rendering
UpdateCurrentFrameDataCache() is called once per Tick, iterating all character indices and calling GetCurrentFrameData() for each revealed character.
Editor Preview
SetPreviewFrameDataOverride() allows injecting frame data overrides from external sources (the Sequencer editor). When HasPreviewFrameData() returns true, GetCurrentFrameData() returns the injected preview data instead of real-time evaluation results — this is the key mechanism for real-time preview when dragging the Sequencer playhead in the editor.
Animation Completion & Delegates
| Delegate | Signature | Trigger |
|---|---|---|
OnCharacterRevealed | (int32 CharIdx, TCHAR Char) | Broadcast each time a character is revealed |
OnAnimationComplete | () | Broadcast when animation fully completes |
In loop modes (Loop/PingPong), OnLoopRestart() is called when the animation completes to reset state and continue playing — OnAnimationComplete is NOT broadcast. The completion event is only broadcast on final completion when MaxLoopCount is reached.
Debounce Protection
MaxAdvanceFramesPerTick (constant 5) limits the maximum frames advanced per Tick, preventing a single frame from skipping the entire animation due to a hitch. When DeltaTime is so large that more than 5 frames would advance, only 5 are advanced, with the remainder caught up in subsequent Ticks.