富文本标签语法

Texturge Beta 使用类 XML 标签为文本内容绑定动画。标签由 FTagParser 解析为 FTagNode 树,再由 FRenderTreeBuilder 结合 UTextAnimationDataAsset 构建渲染树(FRenderNode),供 UAnimatedRichTextBlock 使用。

NOTE

这不是 XML——闭标签统一使用 </>(关闭最近打开的标签),不需要重复标签名。

标签结构

动画标签

<anim id="wave">内容</>
  • 开标签<anim id="...">——标签名为 anim(忽略大小写),通过 id 属性匹配 UTextAnimationDataAsset::Entries[] 中的 FAnimationEntry::TagName(同样忽略大小写)
  • 闭标签</>——关闭最近打开的标签,无需写标签名
  • 未匹配到任何 Entry 的 <anim> 标签按 StyleLayer 处理并输出 [RenderTreeBuilder] <anim id="..."> 未匹配到任何 Entry 警告
  • 内容可以是纯文本或其他嵌套标签

自闭合标签

<img src="icon" />

自闭合且无子节点的标签分类为 Decorator(装饰器节点,如内嵌图片)。

语法规则

  1. 开标签使用尖括号 <> 包裹:<标签名 属性="值">
  2. 闭标签统一为 </>——每次关闭最近打开的一个标签(嵌套时按从内到外的顺序书写多个 </> 或用显式闭标签关闭样式标签)
  3. 自闭合标签以 /> 结尾
  4. 标签名由 [A-Za-z0-9_-] 组成,最长 32 字符
  5. 属性支持带引号与无引号两种写法,属性值支持转义(&lt; &gt; &amp; &quot; &apos;
  6. 未闭合的标签在解析结束时自动闭合(AutoCloseRemaining,每条一条 UE_LOG 警告)

嵌套示例

<anim id="wave"><color style="red">彩色波浪文字</color></>
<anim id="typewriter">逐字显示<anim id="shake">并抖动</>的文字</>
  • <color>...</color> 是样式标签,使用显式闭标签(引擎富文本样式系统要求)
  • <anim> 标签统一使用 </> 关闭(内层 </> 先关闭 shake,外层 </> 再关闭 typewriter

解析管线

第一步:FTagParser

FTagParser::Parse(const FString& InRichText, FString& OutPlainText) 将原始文本解析为 FTagNode 树:

  1. 逐字符扫描,遇到 < 进入标签解析模式
  2. 识别标签名(ParseTagName)与属性(ParseAttributes
  3. 无效标签不报错——< 视为纯文本输出
  4. </> 关闭最近打开的标签;</名称> 从栈顶向下查找匹配名称的标签
  5. AutoCloseRemaining 自动闭合未配对标签(每条一条 UE_LOG 警告)
  6. 标签剥离 + 转义解码后输出 OutPlainText(纯文本,含码点索引基准)

第二步:FRenderTreeBuilder

FRenderTreeBuilder::Build(TSharedPtr<FTagNode> ParseRoot, const FString& PlainText, UTextAnimationDataAsset* AnimationData) 将解析树转换为渲染树:

  1. DFS 遍历 FTagNode 树,兄弟节点间自动插入 TextContent 节点(携带纯文本 Range)
  2. ClassifyNode 对每个标签节点分类(见下)
  3. AnimationLayer 节点通过 FindEntryIndex 解析 EntryIndex(在 Entries[] 中按 id 匹配)
  4. 输出 TSharedPtr<FRenderNode> 根节点

辅助序列化

函数说明
SerializeToDisplayText(FRenderNode)渲染树 → 显示文本:AnimationLayer 跳过标签输出子节点、StyleLayer 输出完整标签包裹、Decorator 输出自闭合标签
SerializeSubstring(FRenderNode, int32 MaxCharIndex)按字形索引截取已揭示部分,样式包裹,自动反转义
images/tag-parse-pipeline.png — 标签解析流程示意图:原始富文本字符串 → FTagParser 解析为 FTagNode 树 → FRenderTreeBuilder 结合 DataAsset 分类构建 FRenderNode 渲染树,标注 RangeBegin/RangeEnd 纯文本索引与 EntryIndex 匹配 — Tag parsing pipeline: raw rich text → FTagParser parses into FTagNode tree → FRenderTreeBuilder classifies with DataAsset into FRenderNode render tree, with RangeBegin/RangeEnd plain-text indices and EntryIndex matching annotated

节点分类

FRenderTreeBuilder::ClassifyNode 按以下顺序判定(ERenderNodeType):

枚举值判定条件说明
AnimationLayer标签名 anim(忽略大小写)且 id 属性匹配某 Entry动画层,EntryIndex 指向 Entries[]
Decorator自闭合(/>)且无子节点装饰器节点(如图片占位)
StyleLayer其他一切标签样式层,委托给 UE5 富文本样式系统处理
TextContent纯文本叶子(非标签节点)
Unknown空标签名 / 无效节点异常状态

UTextAnimationDataAsset

核心映射资产,将动画 id 映射到动画蓝图:

属性类型说明
EntriesTArray<FAnimationEntry>标签 → 动画蓝图映射数组

FAnimationEntry 字段

字段类型说明
TagNameFName匹配的动画 id(与 <anim id="..."> 比较,忽略大小写)
TypeTObjectPtr<UTextAnimationBlueprint>动画蓝图资产引用
ParameterOverridesTMap<FName, float>Float 参数覆盖
IntParameterOverridesTMap<FName, int32>Int 参数覆盖
BoolParameterOverridesTMap<FName, bool>Bool 参数覆盖
VectorParameterOverridesTMap<FName, FVector>Vector 参数覆盖
ColorParameterOverridesTMap<FName, FLinearColor>Color 参数覆盖
Vector2DParameterOverridesTMap<FName, FVector2D>Vector2D 参数覆盖
bHideFirstFramebool顺序播放时未执行条目提前透明

6 组覆盖映射与 UAnimParameterOverrides 类型结构一一对应,烘焙时经 ApplyAnimParamOverridesToInstance<FAnimationEntry> 写入临时实例。

UAnimatedRichTextBlock

UAnimatedRichTextBlock(继承 URichTextBlock)驱动富文本动画:

  1. 控件持有 AnimationDataUTextAnimationDataAsset*)属性
  2. 文本变更时内部通过私有 FRichTextMarshaller 完成解析与渲染树构建
  3. 每个 AnimationLayer 节点实例化对应的动画条目,UTextAnimator 编排多条目播放(bSequentialPlayback 顺序 / 同时)
  4. 样式标签(StyleLayer)与引擎 URichTextBlock 的样式集、装饰器、数据表机制协同
// C++ 设置流程
AnimatedRichTextBlock->AnimationData = MyDataAsset;
AnimatedRichTextBlock->SetText(FText::FromString(
    TEXT("Hello <anim id=\"wave\">World</>!")));

嵌套规则

  1. 嵌套必须合法配对(内部标签的闭标签先于外部标签出现)
  2. anim 标签可嵌套其他样式标签(<anim id="wave"><color ...>...</color></>
  3. 未匹配的 <anim> 按 StyleLayer 处理(保留标签,文本无动画)

NOTE

嵌套深度无硬性上限,但过深的标签结构会增加 FTagParser 解析开销与渲染树构建成本,建议控制在 4 层以内。

合法嵌套示例

<anim id="wave"><color style="red">彩色波浪文字</color></>
<anim id="typewriter">逐字显示<anim id="shake">并抖动</>的文字</>
images/decorator-injection.png — 装饰器节点示意图:渲染树中的 Decorator 叶子(自闭合标签如 )与 TextContent 叶子并列,AnimationLayer 节点包裹其子节点 — Decorator node diagram: Decorator leaf (self-closing tags like ) and TextContent leaves side by side in the render tree, AnimationLayer nodes wrapping their children