文本动画器
文本动画器(UTextAnimator)是 Texturge 的运行时动画管线引擎。它作为独立 UObject 运行,不依赖 Actor 或 ActorComponent 生命周期,被 Slate 控件持有并通过 Tick() 驱动逐帧步进。
职责
输入 处理 输出
──────────────────────────────────────────────────────────────────────
PlainText / RichText → ExecutePipeline() → FAnimationFrameData[]
TextAnimationBlueprint → 编译 AnimInstance → DisplayRichText
AnimationDataAsset → 构建 RenderTree → OnCharacterRevealed 委托
PlayMode / CPS / Speed → AdvanceFrame() → OnAnimationComplete 委托
两条管线
动画器支持两条互斥的动画管线,根据输入类型自动选择:
纯文本管线
用于 UAnimatedTextBlock,单一动画蓝图驱动整段文本:
- 断字 — 对 PlainText 执行字符断点分析(考虑 CJK 代理对等 Unicode 边界)
- 编译 — 从
TextAnimationBlueprint创建UTextAnimInstance并编译 - 帧生成 —
EvaluateTime()在每个时间点采样所有轨道,混合生成每字符帧数据 - 时间计算 — 根据
PlaybackMode(Duration / CPS)计算每字符揭示时间
多格式文本管线
用于 UAnimatedRichTextBlock,通过渲染树驱动多层动画:
- 解析 —
FTagParser将多格式文本解析为标签树 - 构建渲染树 —
FRenderTreeBuilder分类节点(AnimationLayer / StyleLayer / TextContent / Decorator) - 逐层编译 — 每个 AnimationLayer 节点对应一个
UTextAnimInstance - 逐层帧生成 — 每个动画层的帧数据独立求值
- 时间计算 — 基于所有层中最长的总时长
播放控制
播放模式
| 模式 | 枚举值 | 行为 |
|---|---|---|
| 正常 | Normal | 正向播放,到达末尾时停止 |
| 快进 | FastForward | 正向加速播放(由 PlaySpeedMultiplier 控制倍率) |
| 倒放 | Reverse | 反向逐字隐藏 |
| 乒乓 | PingPong | 正向→反向→正向循环 |
| 循环 | Loop | 播放完毕后从头开始(由 MaxLoopCount 控制次数,≤0 无限循环) |
时序模式
| 模式 | 说明 |
|---|---|
| Duration | 固定总时长模式。动画在 TotalDuration 秒内完成,每字符时间均匀分配。 |
| CPS | 每秒字符数模式。以固定速率(CPS,默认 10 字符/秒)揭示字符。 |
公开 API
// 输入
void SetPlainText(const FString& InText);
void SetRichText(const FString& InRichText);
void SetAnimationData(UTextAnimationDataAsset* InData);
void SetAnimationBlueprint(UTextAnimationBlueprint* InBlueprint);
// 控制
void StartAnimating(bool bRevealAll = false);
void TickAnimation(float InDeltaTime);
void Pause();
void Resume();
void Stop();
void SkipToEnd();
// 查询
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;
帧数据缓存
CurrentFrameDataCache 是动画器每 Tick 更新的核心输出——一个 TArray<FAnimationFrameData>,长度等于文本字符数。此缓存直接供 Slate 控件的 OnPaint() 消费:
- 已揭示字符:缓存中为求值后的动画帧数据
- 未揭示字符:缓存中为恒等帧(
Opacity=0),不渲染
UpdateCurrentFrameDataCache() 每 Tick 调用一次,遍历所有字符索引,对每个已揭示字符调用 GetCurrentFrameData() 获取帧数据。
编辑器预览
SetPreviewFrameDataOverride() 允许从外部(Sequencer 编辑器)注入帧数据覆盖缓存。当 HasPreviewFrameData() 返回 true 时,GetCurrentFrameData() 返回注入的预览数据而非实时求值结果——这是编辑器中 Sequencer 拖动时间轴时实时预览的关键机制。
动画完成与委托
| 委托 | 签名 | 触发时机 |
|---|---|---|
OnCharacterRevealed | (int32 CharIdx, TCHAR Char) | 每揭示一个字符时广播 |
OnAnimationComplete | () | 动画完全结束时广播 |
循环模式下(Loop/PingPong),动画完成时调用 OnLoopRestart() 重置状态并继续播放,不广播 OnAnimationComplete。当 MaxLoopCount 达到指定次数后,最终结束时才广播完成事件。
防抖保护
MaxAdvanceFramesPerTick(常量 5)限制每 Tick 最大前进帧数,防止因卡顿导致一帧跳过整个动画。当 DeltaTime 过大使得本应前进超过 5 帧时,仅前进 5 帧,剩余帧在后续 Tick 中补足。