数据结构参考
Texturge 定义了一组数据结构,用于在编译期、运行时与蓝图之间传递动画数据。按照是否可由蓝图反射分为 UENUM / USTRUCT 与纯 C++ 结构两类。
枚举类型
EAnimationStage
UENUM(BlueprintType, meta = (DisplayName = "动画阶段"))
enum class EAnimationStage : uint8
{
Intro UMETA(DisplayName = "入场"),
Default UMETA(DisplayName = "默认"),
Outro UMETA(DisplayName = "出场"),
None UMETA(Hidden),
GlyphEditor UMETA(Hidden)
};
| 值 | 说明 |
|---|---|
Intro | 入场阶段 |
Default | 默认阶段 |
Outro | 出场阶段 |
None | 空阶段(Hidden) |
GlyphEditor | 编辑器专用字形编辑模式(Hidden) |
ETextAnimationBlendMode
UENUM(BlueprintType, meta = (DisplayName = "文本动画混合模式"))
enum class ETextAnimationBlendMode : uint8
{
Additive UMETA(DisplayName = "加法混合"),
Override UMETA(DisplayName = "覆盖混合"),
Multiply UMETA(DisplayName = "乘法混合"),
CrossFade UMETA(DisplayName = "交叉淡化")
};
| 值 | 说明 |
|---|---|
Additive | 层结果叠加到基础值 |
Override | 层结果覆盖基础值 |
Multiply | 层结果与基础值相乘 |
CrossFade | 交叉淡化 |
EPlayMode
UENUM(BlueprintType, meta = (DisplayName = "播放方向"))
enum class EPlayMode : uint8
{
Forward UMETA(DisplayName = "前向"),
Reverse UMETA(DisplayName = "反向"),
PingPong UMETA(DisplayName = "乒乓")
};
NOTE
EPlayMode只有前向 / 反向 / 乒乓三种方向语义,没有 Loop 枚举值——循环由FPerLayerCurves::NumberOfLoops(0 = 无限循环)独立控制。
EPlaybackMode(UTextAnimator)
UENUM(BlueprintType, meta = (DisplayName = "播放时序模式"))
enum class EPlaybackMode : uint8
{
Duration UMETA(DisplayName = "持续时间模式"),
CPS UMETA(DisplayName = "每秒字形数模式")
};
| 值 | 说明 |
|---|---|
Duration | 按总时长控制播放速度 |
CPS | 按每秒字形数控制(SetCPS,下限 1.0) |
EStagePlayDirection / EStagePlaybackState
UENUM(BlueprintType)
enum class EStagePlayDirection : uint8
{
Forward, Reverse, PingPong
};
UENUM(BlueprintType)
enum class EStagePlaybackState : uint8
{
Stopped, Playing, Paused, Complete
};
ETextAnimationValueType / ETextAnimationTrackType
UENUM(BlueprintType, meta = (DisplayName = "文本动画值类型"))
enum class ETextAnimationValueType : uint8
{
Float, Vector2D, Color
};
UENUM(BlueprintType, meta = (DisplayName = "文本动画轨道类型"))
enum class ETextAnimationTrackType : uint8
{
Opacity = 0, // Float
LetterSpacing = 1, // Float
Rotation = 2, // Float
Translation = 10, // Vector2D
Scale = 11, // Vector2D
Shear = 12, // Vector2D
Pivot = 13, // Vector2D
ShadowOffset = 14, // Vector2D
Color = 20, // Color
ShadowColor = 21 // Color
};
10 种轨道类型展开为 21 条曲线通道:3(Float)+ 5 × 2(Vector2D 分量)+ 2 × 4(Color 分量)。
EGlyphOverrideOperation
UENUM(BlueprintType, meta = (DisplayName = "字形微调操作"))
enum class EGlyphOverrideOperation : uint8
{
Multiply UMETA(DisplayName = "乘算"),
Divide UMETA(DisplayName = "除算"),
Add UMETA(DisplayName = "加算"),
Subtract UMETA(DisplayName = "减算")
};
ERenderNodeType(非 UENUM)
enum class ERenderNodeType : uint8
{
Unknown,
AnimationLayer, // 动画标签层(匹配 DataAsset Entry)
StyleLayer, // 富文本样式层
TextContent, // 纯文本叶子
Decorator // 自闭合装饰器(如图片)
};
EAnimParamType(非 UENUM)
enum class EAnimParamType : uint8
{
Float, Int, Bool, Vector, Color, Vector2D
};
由 GetAnimParamType(const FProperty*) 从属性类型推导,驱动模板分派。
ETexturgePreviewSource(Editor 模块)
enum class ETexturgePreviewSource : uint8
{
None, // 无预览 — 显示静止文本
StagePreview, // 独立时钟循环播放三阶段
GlyphAnimation // Sequencer 播放头驱动
};
USTRUCT 结构体
FGlyphScheduleInfo
编译时每个字形(Unicode 码点)的调度配置。前 6 个字段由 BuildScheduleInfos 系统预填(只读),后 2 个由设计师设置(可读写)。
USTRUCT(BlueprintType, meta = (DisplayName = "字形调度信息"))
struct TEXTURGE_API FGlyphScheduleInfo
{
// —— 系统预填(BlueprintReadOnly)——
int32 GlyphIndex; // 字形在文本中的索引
FString GlyphString; // 单字形字符串(支持 U+FFFC 内嵌对象占位符)
bool bIsWhitespace; // 空白字形
bool bIsPunctuation; // 标点符号
bool bIsCJK; // 中日韩文字
bool bIsDecorator; // 内嵌对象装饰器
// —— 设计师设置(BlueprintReadWrite)——
float StartDelay; // 动画起始延迟(秒)
bool bSkipAnimation; // 跳过此字形在本阶段的动画
};
NOTE
该结构已移除早期版本的
ValueScale/ValueOffset/SeedJitter等字段——曲线级微调统一由FGlyphCurveOverride有序操作步骤承担。
FGlyphOverrideStep 系列
USTRUCT(BlueprintType, meta = (DisplayName = "Float 微调步骤"))
struct FGlyphFloatOverrideStep
{
EGlyphOverrideOperation Operation = EGlyphOverrideOperation::Multiply;
float Value = 1.0f;
};
USTRUCT(BlueprintType, meta = (DisplayName = "Vec2 微调步骤"))
struct FGlyphVec2OverrideStep
{
EGlyphOverrideOperation Operation = EGlyphOverrideOperation::Multiply;
FVector2D Value = FVector2D(1.0f, 1.0f);
};
USTRUCT(BlueprintType, meta = (DisplayName = "颜色微调步骤"))
struct FGlyphColorOverrideStep
{
EGlyphOverrideOperation Operation = EGlyphOverrideOperation::Multiply;
FLinearColor Value = FLinearColor::White;
};
FGlyphCurveOverride
字形曲线微调覆盖——10 个属性的有序操作步骤集合。Layer 级在烘焙 STEP 1.5 逐层改写曲线键;Factory 级烘焙进 FBakedGlyph::FactoryOverride,求值时对混合后的最终状态整体应用一次。
| 属性 | 步骤数组类型 |
|---|---|
| 不透明度 / 字间距 / 旋转 | TArray<FGlyphFloatOverrideStep> |
| 平移 / 缩放 / 修剪 / 枢纽 / 阴影偏移 | TArray<FGlyphVec2OverrideStep> |
| 染色 / 阴影染色 | TArray<FGlyphColorOverrideStep> |
FGlyphAnimationState
运行时单字形渲染状态。默认构造等于恒等状态(无变换、白色不透明、阴影透明)。
USTRUCT(BlueprintType, meta = (DisplayName = "字形动画状态"))
struct TEXTURGE_API FGlyphAnimationState
{
// 变换
FVector2D PositionOffset; // 位置偏移(像素)
FVector2D Scale; // 缩放(默认 1,1)
float Rotation; // 旋转(度)
FVector2D Shear; // 剪切变换
FVector2D Pivot; // 枢轴(0~1 归一化,默认 0.5,0.5)
// 颜色与不透明度
FLinearColor Color; // 染色:RGB = 染色色,A = 染色占比(0~1)
float Opacity; // 不透明度(默认 1.0)
// 阴影
FVector2D ShadowOffset; // 阴影偏移(像素)
FLinearColor ShadowColor; // 阴影颜色(默认 Transparent)
// 排版
float LetterSpacing; // 字间距(像素)
static FGlyphAnimationState Identity();
};
NOTE
染色语义:
Color.A是染色占比而非透明度——A=1.0完全染色、A=0.5与文本色 1:1、A=0.0不染色。恒等值Transparent (0,0,0,0)。染色不改变字形透明度(透明度由Opacity通道单独控制)。混色算法:染色采用 Pigment-Based Mixing(PBM) 物理颜料混色(
Texturge::GlyphRender::ApplyTint)——基于 Kubelka–Munk(K–M)双流理论 + Duncan 浓度叠加、常数参考标定于 Mixbox 2.0 的实时 RGB 颜料混色,在 sRGB 显示值空间工作,无光谱数据、无查找表、O(1) 常数时间,严格交换对称F(b, t, α) ≡ F(t, b, 1−α)。效果贴合真实颜料:黄+蓝变绿,互补色对混合产生泥色(橄榄/棕/暗紫)而非灰色,深色加白得到饱和浅色而非灰白。算法以 MIT 协议开源(pigment-based-mixing)。
FPerLayerCurves
单层曲线数据——21 条 TArray<FRichCurveKey> + 播放元数据。这是 FRichCurve 非 USTRUCT 约束下完全 UPROPERTY 化的存储方案:求值时临时构建 FRichCurve。
USTRUCT()
struct TEXTURGE_API FPerLayerCurves
{
// 播放元数据
ETextAnimationBlendMode BlendMode = ETextAnimationBlendMode::Override;
EPlayMode PlayDirection = EPlayMode::Forward;
int32 NumberOfLoops = 1; // 0 = 无限循环
float PlaySpeedMultiplier = 1.0f;
float LayerDuration = 0.0f; // 烘焙时预计算(max key - min key)
// 21 条曲线通道(TArray<FRichCurveKey>)
// Float(3): Curve_Opacity, Curve_LetterSpacing, Curve_Rotation
// Vector2D(10): Curve_TranslationX/Y, Curve_ScaleX/Y,
// Curve_ShearX/Y, Curve_PivotX/Y, Curve_ShadowOffsetX/Y
// Color(8): Curve_ColorR/G/B/A, Curve_ShadowColorR/G/B/A
bool IsEmpty() const; // 所有曲线是否全为空
float GetEffectiveDuration() const; // 有限循环 = LayerDuration × Loops;无限 = FLT_MAX
void Evaluate(float LocalTime, FGlyphAnimationState& InOutState) const;
};
Evaluate 语义:
- 仅对
NumKeys > 0的通道求值并组合——空通道不参与,保留InOutState已有值(层间无交叉污染) PlayDirection != Forward且LayerDuration > 0时对LocalTime做时间包裹:Forward = fmod 循环、Reverse = 反向 fmod、PingPong = 来回交替- 组合规则:Override 直接覆盖 / Additive 相加 / Multiply 相乘 / CrossFade 固定 0.5 Lerp 插值
FBakedGlyph
烘焙后的单字形运行时数据。
USTRUCT(BlueprintType, meta = (DisplayName = "烘焙字形"))
struct TEXTURGE_API FBakedGlyph
{
UPROPERTY(BlueprintReadOnly) int32 OriginalIndex; // 原始文本索引
TCHAR Character; // 调试用(非 UPROPERTY)
UPROPERTY(BlueprintReadOnly) float StartTime; // 阶段内相对起始时间(秒)
UPROPERTY(BlueprintReadOnly) float Duration; // 动画持续时间(秒)
UPROPERTY(BlueprintReadOnly) bool bIsStatic; // 跳过动画的静态字形
UPROPERTY(BlueprintReadOnly) FGlyphAnimationState DefaultState; // 静态字形固定状态
UPROPERTY(BlueprintReadOnly) bool bHideFirstFrame;// 动画开始前完全透明
TArray<FPerLayerCurves> LayerCurves; // 逐层曲线(0 最底 → N-1 最顶)
UPROPERTY(BlueprintReadOnly) FGlyphCurveOverride FactoryOverride; // 工厂级微调
void Evaluate(float StageTime, FGlyphAnimationState& OutState) const;
bool HasStarted(float StageTime) const;
bool HasFinished(float StageTime) const;
void AddLayer(const FGlyphCurveSet& Source, ETextAnimationBlendMode InBlendMode);
// 单层兼容辅助: IsCurvesEmpty / GetCurvesTimeRange / CopyCurvesFrom /
// OffsetCurvesBy / BuildCurveSet
};
Evaluate 语义:
bIsStatic→ 直接返回DefaultState- 尚未开始(
LocalTime < 0)→ 返回恒等状态(不透明度 1 完全显示),仅bHideFirstFrame为真时透明 - 逐层独立求值后按 BlendMode 组合:
OutState = Identity()→ 每层Layer.Evaluate(LocalTime, OutState)
FBakedStage
烘焙后的单阶段数据。完全由 UPROPERTY 构成,支持作为 BlueprintNativeEvent 返回类型(Kismet 编译器安全)。
USTRUCT(BlueprintType, meta = (DisplayName = "烘焙阶段"))
struct TEXTURGE_API FBakedStage
{
EAnimationStage StageType = EAnimationStage::None;
int32 GlyphCount = 0;
float TotalDuration = 0.0f;
TArray<FBakedGlyph> Glyphs;
bool IsValid() const; // GlyphCount > 0 且数组匹配
int32 GetStartedGlyphCount(float StageTime) const;
};
FStageTransitionConfig
USTRUCT(BlueprintType, meta = (DisplayName = "阶段过渡配置"))
struct TEXTURGE_API FStageTransitionConfig
{
float IntroToDefaultCrossFade = 0.1f; // 入场→默认过渡时长(秒)
float DefaultToOutroCrossFade = 0.1f; // 默认→出场过渡时长(秒)
TEnumAsByte<EEasingFunc::Type> EasingFunc = EEasingFunc::Linear; // 过渡缓动
};
该配置位于 UTextAnimationBlueprint::StageTransitionConfig,参与蓝图指纹哈希。
FAnimationEntry
UTextAnimationDataAsset::Entries[] 的行数据结构——标签名到动画蓝图的映射。
USTRUCT(BlueprintType, meta = (DisplayName = "动画条目"))
struct TEXTURGE_API FAnimationEntry
{
FName TagName; // 富文本标签名
TObjectPtr<UTextAnimationBlueprint> Type; // 动画蓝图资产
TMap<FName, float> ParameterOverrides; // Float 覆盖
TMap<FName, int32> IntParameterOverrides; // Int 覆盖
TMap<FName, bool> BoolParameterOverrides; // Bool 覆盖
TMap<FName, FVector> VectorParameterOverrides; // Vector 覆盖
TMap<FName, FLinearColor> ColorParameterOverrides; // Color 覆盖
TMap<FName, FVector2D> Vector2DParameterOverrides; // Vector2D 覆盖
bool bHideFirstFrame = false; // 顺序播放时未执行条目提前透明
};
FSemanticAnchor
USTRUCT(BlueprintType, meta = (DisplayName = "语义锚点"))
struct TEXTURGE_API FSemanticAnchor
{
FName AnchorId; // 锚点标识符
FString ContextualSnippet; // 上下文片段
int32 SourceOffset; // 源偏移量
};
由 ULocalizationSubsystem::RelocateAnchors 处理:精确匹配(忽略大小写)+ LCS 最长公共子串近似匹配 + 置信度去重(MaxTextLength = 10000 上限)。
FTextAnimationSubTrackData
USTRUCT(meta = (DisplayName = "字形动画子轨道数据"))
struct TEXTURGE_API FTextAnimationSubTrackData
{
ETextAnimationTrackType TrackType = ETextAnimationTrackType::Opacity;
FName DisplayName;
TArray<FMovieSceneFloatChannel> Channels; // Float→1, Vector2D→2, Color→4
};
C++ 结构体(非 USTRUCT)
FBakedAnimation
完整烘焙结果。不是 USTRUCT(内含 TUniquePtr),仅在 C++ 层使用。
struct FBakedAnimation
{
FString SourceText;
int32 GlyphCount = 0;
uint64 TextFingerprint = 0; // CityHash64(SourceText)
uint64 BlueprintFingerprint = 0; // 哈希(路径 + 资产签名 + BP 变量 + CompileCount + 生成类地址)
TUniquePtr<FBakedStage> IntroStage; // 可为 nullptr
TUniquePtr<FBakedStage> DefaultStage; // 通常有效
TUniquePtr<FBakedStage> OutroStage; // 可为 nullptr
const FBakedStage* GetStage(EAnimationStage Stage) const;
bool HasStage(EAnimationStage Stage) const;
EAnimationStage GetFirstValidStage() const; // 优先 Intro,其次 Default
EAnimationStage GetNextStageAfter(EAnimationStage Current) const;
float GetTotalDuration() const; // 所有存在阶段 Duration 之和
};
FGlyphCurveSet
C++ 内部临时计算容器——21 条 FRichCurve(FRichCurve 非 USTRUCT,不能作 UPROPERTY)。
struct TEXTURGE_API FGlyphCurveSet
{
// Float: Opacity, LetterSpacing, Rotation
// Vector2D: TranslationX/Y, ScaleX/Y, ShearX/Y, PivotX/Y, ShadowOffsetX/Y
// Color: ColorR/G/B/A, ShadowColorR/G/B/A
void Evaluate(float Time, FGlyphAnimationState& OutState) const;
void OffsetBy(float Seconds);
void CopyFrom(const FGlyphCurveSet& Source);
TRange<float> GetTimeRange() const;
bool IsEmpty() const;
};
FTagNode / FRenderNode
富文本解析的两级树节点(纯 C++)。
struct TEXTURGE_API FTagNode
{
FString TagName; // 根节点为空
TMap<FString, FString> Attributes;
TArray<TSharedPtr<FTagNode>> Children;
TWeakPtr<FTagNode> Parent; // 弱指针防循环引用
int32 RangeBegin; // 纯文本起始索引(包含)
int32 RangeEnd; // 纯文本结束索引(不包含)
bool bIsSelfClosing = false;
};
struct TEXTURGE_API FRenderNode
{
ERenderNodeType NodeType = ERenderNodeType::Unknown;
FString TagName;
TMap<FString, FString> Attributes;
TArray<TSharedPtr<FRenderNode>> Children;
int32 RangeBegin = 0;
int32 RangeEnd = 0;
int32 EntryIndex = INDEX_NONE; // AnimationLayer: Entries 索引
FString Text; // TextContent: 叶子文本
};
UAnimParameterOverrides(UCLASS)
UCLASS(meta = (DisplayName = "动画参数覆盖"))
class TEXTURGE_API UAnimParameterOverrides : public UObject
{
TMap<FName, float> FloatOverrides;
TMap<FName, int32> IntOverrides;
TMap<FName, bool> BoolOverrides;
TMap<FName, FVector> VectorOverrides;
TMap<FName, FLinearColor> ColorOverrides;
TMap<FName, FVector2D> Vector2DOverrides;
};
21 通道映射总表
| # | 曲线字段 | 轨道类型 | 所属状态属性 |
|---|---|---|---|
| 1 | Curve_Opacity | Opacity | Opacity |
| 2 | Curve_LetterSpacing | LetterSpacing | LetterSpacing |
| 3 | Curve_Rotation | Rotation | Rotation |
| 4 | Curve_TranslationX | Translation | PositionOffset.X |
| 5 | Curve_TranslationY | Translation | PositionOffset.Y |
| 6 | Curve_ScaleX | Scale | Scale.X |
| 7 | Curve_ScaleY | Scale | Scale.Y |
| 8 | Curve_ShearX | Shear | Shear.X |
| 9 | Curve_ShearY | Shear | Shear.Y |
| 10 | Curve_PivotX | Pivot | Pivot.X |
| 11 | Curve_PivotY | Pivot | Pivot.Y |
| 12 | Curve_ShadowOffsetX | ShadowOffset | ShadowOffset.X |
| 13 | Curve_ShadowOffsetY | ShadowOffset | ShadowOffset.Y |
| 14 | Curve_ColorR | Color | Color.R |
| 15 | Curve_ColorG | Color | Color.G |
| 16 | Curve_ColorB | Color | Color.B |
| 17 | Curve_ColorA | Color | Color.A(染色占比) |
| 18 | Curve_ShadowColorR | ShadowColor | ShadowColor.R |
| 19 | Curve_ShadowColorG | ShadowColor | ShadowColor.G |
| 20 | Curve_ShadowColorB | ShadowColor | ShadowColor.B |
| 21 | Curve_ShadowColorA | ShadowColor | ShadowColor.A |
TIP
FGlyphAnimationState是 10 字段(非 21 字段)——21 是指曲线通道数,Vector2D 与 Color 类型属性各自展开为 2 / 4 条通道。新增通道由GlyphChannelTable.h的 21 行成员指针注册表驱动(static_assert(21)),新增一条通道只需改动约 5 处。
数据结构关系图
UTextAnimationBlueprint
├── GlyphAnimations: TArray<UGlyphAnimation*> ← Sequencer 曲线资产
│ └── MovieScene → UMovieSceneGlyphAnimationTrack
│ └── UMovieSceneGlyphAnimationSection[](子轨道)
│ └── FTextAnimationSubTrackData.Channels (FMovieSceneFloatChannel)
├── StageTransitionConfig
└── [Compile] → UTextAnimationBlueprintGeneratedClass
└── FAnimationCompiler::BakeAllStages(BP, Text, Customizer)
├── ExtractCurves → FGlyphCurveSet(21 FRichCurve,缓存)
├── BuildScheduleInfos → TArray<FGlyphScheduleInfo>
└── UGlyphAnimationLayer / UGlyphAnimationFactory
└── Bake() → FBakedStage
└── FBakedAnimation
├── IntroStage / DefaultStage / OutroStage → FBakedStage
│ └── Glyphs: TArray<FBakedGlyph>
│ └── LayerCurves: TArray<FPerLayerCurves>(21 通道 × 每层)
│ └── FactoryOverride: FGlyphCurveOverride
├── TextFingerprint / BlueprintFingerprint (uint64)
UTextAnimationStageController::EvaluateAllGlyphs
└── FBakedGlyph::Evaluate(StageTime, OutState)
└── 逐层 FPerLayerCurves::Evaluate(LocalTime, OutState) → 按 BlendMode 组合
└── FGlyphAnimationState
内存布局考量
FBakedAnimation使用TUniquePtr持有三个舞台,move 语义支持 Double-Buffer 发布切换FPerLayerCurves是最大结构体——21 个TArray<FRichCurveKey>,求值时临时构建FRichCurve(性能代价微小,未来可缓存 + 脏位优化)FGlyphAnimationState是紧凑 POD 类型(10 个成员,约 64 字节),适合栈分配与批量处理- 运行时热路径(
EvaluateAllGlyphs→FBakedGlyph::Evaluate)全程栈变量,无堆分配
TIP
热路径中始终使用栈分配的
FGlyphAnimationState变量;避免在循环内创建临时TArray容器以最大化吞吐量。大文本 + 多层时关注FPerLayerCurves::Evaluate的临时曲线构建开销。