富文本标签语法
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(装饰器节点,如内嵌图片)。
语法规则
- 开标签使用尖括号
<>包裹:<标签名 属性="值"> - 闭标签统一为
</>——每次关闭最近打开的一个标签(嵌套时按从内到外的顺序书写多个</>或用显式闭标签关闭样式标签) - 自闭合标签以
/>结尾 - 标签名由
[A-Za-z0-9_-]组成,最长 32 字符 - 属性支持带引号与无引号两种写法,属性值支持转义(
<>&"') - 未闭合的标签在解析结束时自动闭合(
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 树:
- 逐字符扫描,遇到
<进入标签解析模式 - 识别标签名(
ParseTagName)与属性(ParseAttributes) - 无效标签不报错——
<视为纯文本输出 </>关闭最近打开的标签;</名称>从栈顶向下查找匹配名称的标签AutoCloseRemaining自动闭合未配对标签(每条一条UE_LOG警告)- 标签剥离 + 转义解码后输出
OutPlainText(纯文本,含码点索引基准)
第二步:FRenderTreeBuilder
FRenderTreeBuilder::Build(TSharedPtr<FTagNode> ParseRoot, const FString& PlainText, UTextAnimationDataAsset* AnimationData) 将解析树转换为渲染树:
- DFS 遍历
FTagNode树,兄弟节点间自动插入TextContent节点(携带纯文本 Range) ClassifyNode对每个标签节点分类(见下)AnimationLayer节点通过FindEntryIndex解析EntryIndex(在Entries[]中按id匹配)- 输出
TSharedPtr<FRenderNode>根节点
辅助序列化:
| 函数 | 说明 |
|---|---|
SerializeToDisplayText(FRenderNode) | 渲染树 → 显示文本:AnimationLayer 跳过标签输出子节点、StyleLayer 输出完整标签包裹、Decorator 输出自闭合标签 |
SerializeSubstring(FRenderNode, int32 MaxCharIndex) | 按字形索引截取已揭示部分,样式包裹,自动反转义 |
节点分类
FRenderTreeBuilder::ClassifyNode 按以下顺序判定(ERenderNodeType):
| 枚举值 | 判定条件 | 说明 |
|---|---|---|
AnimationLayer | 标签名 anim(忽略大小写)且 id 属性匹配某 Entry | 动画层,EntryIndex 指向 Entries[] |
Decorator | 自闭合(/>)且无子节点 | 装饰器节点(如图片占位) |
StyleLayer | 其他一切标签 | 样式层,委托给 UE5 富文本样式系统处理 |
TextContent | — | 纯文本叶子(非标签节点) |
Unknown | 空标签名 / 无效节点 | 异常状态 |
UTextAnimationDataAsset
核心映射资产,将动画 id 映射到动画蓝图:
| 属性 | 类型 | 说明 |
|---|---|---|
Entries | TArray<FAnimationEntry> | 标签 → 动画蓝图映射数组 |
FAnimationEntry 字段
| 字段 | 类型 | 说明 |
|---|---|---|
TagName | FName | 匹配的动画 id(与 <anim id="..."> 比较,忽略大小写) |
Type | TObjectPtr<UTextAnimationBlueprint> | 动画蓝图资产引用 |
ParameterOverrides | TMap<FName, float> | Float 参数覆盖 |
IntParameterOverrides | TMap<FName, int32> | Int 参数覆盖 |
BoolParameterOverrides | TMap<FName, bool> | Bool 参数覆盖 |
VectorParameterOverrides | TMap<FName, FVector> | Vector 参数覆盖 |
ColorParameterOverrides | TMap<FName, FLinearColor> | Color 参数覆盖 |
Vector2DParameterOverrides | TMap<FName, FVector2D> | Vector2D 参数覆盖 |
bHideFirstFrame | bool | 顺序播放时未执行条目提前透明 |
6 组覆盖映射与 UAnimParameterOverrides 类型结构一一对应,烘焙时经 ApplyAnimParamOverridesToInstance<FAnimationEntry> 写入临时实例。
UAnimatedRichTextBlock
UAnimatedRichTextBlock(继承 URichTextBlock)驱动富文本动画:
- 控件持有
AnimationData(UTextAnimationDataAsset*)属性 - 文本变更时内部通过私有
FRichTextMarshaller完成解析与渲染树构建 - 每个
AnimationLayer节点实例化对应的动画条目,UTextAnimator编排多条目播放(bSequentialPlayback顺序 / 同时) - 样式标签(StyleLayer)与引擎
URichTextBlock的样式集、装饰器、数据表机制协同
// C++ 设置流程
AnimatedRichTextBlock->AnimationData = MyDataAsset;
AnimatedRichTextBlock->SetText(FText::FromString(
TEXT("Hello <anim id=\"wave\">World</>!")));
嵌套规则
- 嵌套必须合法配对(内部标签的闭标签先于外部标签出现)
anim标签可嵌套其他样式标签(<anim id="wave"><color ...>...</color></>)- 未匹配的
<anim>按 StyleLayer 处理(保留标签,文本无动画)
NOTE
嵌套深度无硬性上限,但过深的标签结构会增加
FTagParser解析开销与渲染树构建成本,建议控制在 4 层以内。
合法嵌套示例
<anim id="wave"><color style="red">彩色波浪文字</color></>
<anim id="typewriter">逐字显示<anim id="shake">并抖动</>的文字</>