文本动画器

文本动画器UTextAnimator)是 Texturge 的运行时动画管线引擎。它作为独立 UObject 运行,不依赖 Actor 或 ActorComponent 生命周期,被 Slate 控件持有并通过 Tick() 驱动逐帧步进。

职责

输入                          处理                             输出
──────────────────────────────────────────────────────────────────────
PlainText / RichText    →    ExecutePipeline()          →   FAnimationFrameData[]
TextAnimationBlueprint  →    编译 AnimInstance          →   DisplayRichText
AnimationDataAsset      →    构建 RenderTree            →   OnCharacterRevealed 委托
PlayMode / CPS / Speed  →    AdvanceFrame()             →   OnAnimationComplete 委托

两条管线

动画器支持两条互斥的动画管线,根据输入类型自动选择:

纯文本管线

用于 UAnimatedTextBlock,单一动画蓝图驱动整段文本:

  1. 断字 — 对 PlainText 执行字符断点分析(考虑 CJK 代理对等 Unicode 边界)
  2. 编译 — 从 TextAnimationBlueprint 创建 UTextAnimInstance 并编译
  3. 帧生成EvaluateTime() 在每个时间点采样所有轨道,混合生成每字符帧数据
  4. 时间计算 — 根据 PlaybackMode(Duration / CPS)计算每字符揭示时间

多格式文本管线

用于 UAnimatedRichTextBlock,通过渲染树驱动多层动画:

  1. 解析FTagParser 将多格式文本解析为标签树
  2. 构建渲染树FRenderTreeBuilder 分类节点(AnimationLayer / StyleLayer / TextContent / Decorator)
  3. 逐层编译 — 每个 AnimationLayer 节点对应一个 UTextAnimInstance
  4. 逐层帧生成 — 每个动画层的帧数据独立求值
  5. 时间计算 — 基于所有层中最长的总时长

播放控制

播放模式

模式枚举值行为
正常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 中补足。

images/animator-pipeline-flowchart.png — 动画器完整生命周期流程图:StartAnimating → 管线选择 → 编译轨道 → Tick AdvanceFrame → BroadcastCurrentChar → UpdateCache → Slate OnPaint → 完成或循环