Data Structures Reference
Texturge defines a set of data structures for passing animation data between compile time, runtime, and Blueprint. They are split into UENUM / USTRUCT (Blueprint-reflectable) and pure C++ structs.
Enums
EAnimationStage
UENUM(BlueprintType, meta = (DisplayName = "动画阶段"))
enum class EAnimationStage : uint8
{
Intro UMETA(DisplayName = "入场"),
Default UMETA(DisplayName = "默认"),
Outro UMETA(DisplayName = "出场"),
None UMETA(Hidden),
GlyphEditor UMETA(Hidden)
};
| Value | Description |
|---|---|
Intro | intro stage |
Default | default stage |
Outro | outro stage |
None | empty stage (Hidden) |
GlyphEditor | editor-only glyph editing mode (Hidden) |
ETextAnimationBlendMode
UENUM(BlueprintType, meta = (DisplayName = "文本动画混合模式"))
enum class ETextAnimationBlendMode : uint8
{
Additive UMETA(DisplayName = "加法混合"),
Override UMETA(DisplayName = "覆盖混合"),
Multiply UMETA(DisplayName = "乘法混合"),
CrossFade UMETA(DisplayName = "交叉淡化")
};
| Value | Description |
|---|---|
Additive | layer result added to the base value |
Override | layer result replaces the base value |
Multiply | layer result multiplied with the base value |
CrossFade | cross fade |
EPlayMode
UENUM(BlueprintType, meta = (DisplayName = "播放方向"))
enum class EPlayMode : uint8
{
Forward UMETA(DisplayName = "前向"),
Reverse UMETA(DisplayName = "反向"),
PingPong UMETA(DisplayName = "乒乓")
};
NOTE
EPlayModehas only forward / reverse / pingpong direction semantics — no Loop enum value; looping is controlled independently byFPerLayerCurves::NumberOfLoops(0 = infinite loop).
EPlaybackMode (UTextAnimator)
UENUM(BlueprintType, meta = (DisplayName = "播放时序模式"))
enum class EPlaybackMode : uint8
{
Duration UMETA(DisplayName = "持续时间模式"),
CPS UMETA(DisplayName = "每秒字形数模式")
};
| Value | Description |
|---|---|
Duration | playback speed controlled by the total duration |
CPS | controlled by glyphs per second (SetCPS, floor 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
};
The 10 track types expand into 21 curve channels: 3 (Float) + 5 × 2 (Vector2D components) + 2 × 4 (Color components).
EGlyphOverrideOperation
UENUM(BlueprintType, meta = (DisplayName = "字形微调操作"))
enum class EGlyphOverrideOperation : uint8
{
Multiply UMETA(DisplayName = "乘算"),
Divide UMETA(DisplayName = "除算"),
Add UMETA(DisplayName = "加算"),
Subtract UMETA(DisplayName = "减算")
};
ERenderNodeType (not a UENUM)
enum class ERenderNodeType : uint8
{
Unknown,
AnimationLayer, // animation tag layer (matches a DataAsset Entry)
StyleLayer, // rich text style layer
TextContent, // plain text leaf
Decorator // self-closing decorator (e.g. image)
};
EAnimParamType (not a UENUM)
enum class EAnimParamType : uint8
{
Float, Int, Bool, Vector, Color, Vector2D
};
Derived from the property type by GetAnimParamType(const FProperty*), driving template dispatch.
ETexturgePreviewSource (Editor module)
enum class ETexturgePreviewSource : uint8
{
None, // no preview — shows static text
StagePreview, // independent clock looping through three stages
GlyphAnimation // Sequencer playhead driven
};
USTRUCTs
FGlyphScheduleInfo
Per-glyph (Unicode code point) scheduling configuration at compile time. The first 6 fields are system-prefilled by BuildScheduleInfos (read-only); the last 2 are set by the designer (read/write).
USTRUCT(BlueprintType, meta = (DisplayName = "字形调度信息"))
struct TEXTURGE_API FGlyphScheduleInfo
{
// —— system-prefilled (BlueprintReadOnly) ——
int32 GlyphIndex; // glyph index in the text
FString GlyphString; // single-glyph string (supports U+FFFC embedded object placeholder)
bool bIsWhitespace; // whitespace glyph
bool bIsPunctuation; // punctuation
bool bIsCJK; // CJK characters
bool bIsDecorator; // embedded object decorator
// —— designer-set (BlueprintReadWrite) ——
float StartDelay; // animation start delay (seconds)
bool bSkipAnimation; // skip this glyph's animation in this stage
};
NOTE
This struct removed early-version fields such as
ValueScale/ValueOffset/SeedJitter— curve-level tweaks are now uniformly handled by theFGlyphCurveOverrideordered operation steps.
FGlyphOverrideStep Family
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
Glyph curve tweak overrides — ordered operation steps for 10 properties. Layer-level rewrites curve keys per layer in bake STEP 1.5; factory-level bakes into FBakedGlyph::FactoryOverride, applied once to the final blended state at evaluation.
| Property | Step array type |
|---|---|
| Opacity / LetterSpacing / Rotation | TArray<FGlyphFloatOverrideStep> |
| Translation / Scale / Shear / Pivot / ShadowOffset | TArray<FGlyphVec2OverrideStep> |
| Tint / Shadow tint | TArray<FGlyphColorOverrideStep> |
FGlyphAnimationState
Runtime single-glyph render state. Default construction equals the identity state (no transform, white opaque, transparent shadow).
USTRUCT(BlueprintType, meta = (DisplayName = "字形动画状态"))
struct TEXTURGE_API FGlyphAnimationState
{
// transform
FVector2D PositionOffset; // position offset (pixels)
FVector2D Scale; // scale (default 1,1)
float Rotation; // rotation (degrees)
FVector2D Shear; // shear transform
FVector2D Pivot; // pivot (0~1 normalized, default 0.5,0.5)
// color & opacity
FLinearColor Color; // tint: RGB = tint color, A = tint share (0~1)
float Opacity; // opacity (default 1.0)
// shadow
FVector2D ShadowOffset; // shadow offset (pixels)
FLinearColor ShadowColor; // shadow color (default Transparent)
// typography
float LetterSpacing; // letter spacing (pixels)
static FGlyphAnimationState Identity();
};
NOTE
Tint semantics:
Color.Ais the tint share, not opacity —A=1.0fully tinted,A=0.51:1 with the text color,A=0.0untinted. Identity valueTransparent (0,0,0,0). Tinting does not change glyph opacity (opacity is controlled separately by theOpacitychannel).Mixing algorithm: tinting uses Pigment-Based Mixing (PBM) physical pigment mixing (
Texturge::GlyphRender::ApplyTint) — a real-time RGB pigment mixing algorithm built on Kubelka–Munk (K–M) two-flux theory + Duncan concentration superposition, with constants reference-calibrated against Mixbox 2.0, operating in sRGB display-value space — no spectral data, no look-up tables, O(1) constant time, strictly exchange-symmetricF(b, t, α) ≡ F(t, b, 1−α). It reproduces real paint behavior: yellow + blue makes green, complementary pairs mix into muddy colors (olive/brown/dark purple) instead of gray, and deep colors lightened with white stay saturated instead of turning gray. The algorithm is open-sourced under the MIT license (pigment-based-mixing).
FPerLayerCurves
Single-layer curve data — 21 TArray<FRichCurveKey>s + playback metadata. This is the fully-UPROPERTY storage scheme under the constraint that FRichCurve is not a USTRUCT: FRichCurve is built temporarily at evaluation.
USTRUCT()
struct TEXTURGE_API FPerLayerCurves
{
// playback metadata
ETextAnimationBlendMode BlendMode = ETextAnimationBlendMode::Override;
EPlayMode PlayDirection = EPlayMode::Forward;
int32 NumberOfLoops = 1; // 0 = infinite loop
float PlaySpeedMultiplier = 1.0f;
float LayerDuration = 0.0f; // precomputed at bake (max key - min key)
// 21 curve channels (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; // whether all curves are empty
float GetEffectiveDuration() const; // finite loop = LayerDuration × Loops; infinite = FLT_MAX
void Evaluate(float LocalTime, FGlyphAnimationState& InOutState) const;
};
Evaluate semantics:
- Only channels with
NumKeys > 0are evaluated and combined — empty channels do not participate, preserving existingInOutStatevalues (no cross-layer contamination) - With
PlayDirection != ForwardandLayerDuration > 0,LocalTimeis time-wrapped: Forward = fmod loop, Reverse = reversed fmod, PingPong = alternating - Combination rules: Override replaces / Additive adds / Multiply multiplies / CrossFade fixed 0.5 Lerp interpolation
FBakedGlyph
Baked single-glyph runtime data.
USTRUCT(BlueprintType, meta = (DisplayName = "烘焙字形"))
struct TEXTURGE_API FBakedGlyph
{
UPROPERTY(BlueprintReadOnly) int32 OriginalIndex; // original text index
TCHAR Character; // debug only (not UPROPERTY)
UPROPERTY(BlueprintReadOnly) float StartTime; // relative start time within the stage (seconds)
UPROPERTY(BlueprintReadOnly) float Duration; // animation duration (seconds)
UPROPERTY(BlueprintReadOnly) bool bIsStatic; // static glyph skipping animation
UPROPERTY(BlueprintReadOnly) FGlyphAnimationState DefaultState; // fixed state for static glyphs
UPROPERTY(BlueprintReadOnly) bool bHideFirstFrame;// fully transparent before the animation starts
TArray<FPerLayerCurves> LayerCurves; // per-layer curves (0 bottom → N-1 top)
UPROPERTY(BlueprintReadOnly) FGlyphCurveOverride FactoryOverride; // factory-level tweaks
void Evaluate(float StageTime, FGlyphAnimationState& OutState) const;
bool HasStarted(float StageTime) const;
bool HasFinished(float StageTime) const;
void AddLayer(const FGlyphCurveSet& Source, ETextAnimationBlendMode InBlendMode);
// single-layer compatibility helpers: IsCurvesEmpty / GetCurvesTimeRange / CopyCurvesFrom /
// OffsetCurvesBy / BuildCurveSet
};
Evaluate semantics:
bIsStatic→ directly returnsDefaultState- Not started (
LocalTime < 0) → returns the identity state (opacity 1 fully visible), transparent only whenbHideFirstFrameis true - Per-layer independent evaluation then combined by BlendMode:
OutState = Identity()→ each layerLayer.Evaluate(LocalTime, OutState)
FBakedStage
Baked single-stage data. Fully UPROPERTY-composed, supporting use as a BlueprintNativeEvent return type (Kismet compiler safe).
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 and arrays match
int32 GetStartedGlyphCount(float StageTime) const;
};
FStageTransitionConfig
USTRUCT(BlueprintType, meta = (DisplayName = "阶段过渡配置"))
struct TEXTURGE_API FStageTransitionConfig
{
float IntroToDefaultCrossFade = 0.1f; // intro→default transition duration (seconds)
float DefaultToOutroCrossFade = 0.1f; // default→outro transition duration (seconds)
TEnumAsByte<EEasingFunc::Type> EasingFunc = EEasingFunc::Linear; // transition easing
};
This configuration lives on UTextAnimationBlueprint::StageTransitionConfig and participates in the Blueprint fingerprint hash.
FAnimationEntry
The row data structure of UTextAnimationDataAsset::Entries[] — mapping tag names to animation Blueprints.
USTRUCT(BlueprintType, meta = (DisplayName = "动画条目"))
struct TEXTURGE_API FAnimationEntry
{
FName TagName; // rich text tag name
TObjectPtr<UTextAnimationBlueprint> Type; // animation Blueprint asset
TMap<FName, float> ParameterOverrides; // Float overrides
TMap<FName, int32> IntParameterOverrides; // Int overrides
TMap<FName, bool> BoolParameterOverrides; // Bool overrides
TMap<FName, FVector> VectorParameterOverrides; // Vector overrides
TMap<FName, FLinearColor> ColorParameterOverrides; // Color overrides
TMap<FName, FVector2D> Vector2DParameterOverrides; // Vector2D overrides
bool bHideFirstFrame = false; // entries not yet executed are transparent during sequential playback
};
FSemanticAnchor
USTRUCT(BlueprintType, meta = (DisplayName = "语义锚点"))
struct TEXTURGE_API FSemanticAnchor
{
FName AnchorId; // anchor identifier
FString ContextualSnippet; // contextual snippet
int32 SourceOffset; // source offset
};
Processed by ULocalizationSubsystem::RelocateAnchors: exact matching (case-insensitive) + LCS longest-common-substring approximate matching + confidence dedup (MaxTextLength = 10000 cap).
FTextAnimationSubTrackData
USTRUCT(meta = (DisplayName = "字形动画子轨道数据"))
struct TEXTURGE_API FTextAnimationSubTrackData
{
ETextAnimationTrackType TrackType = ETextAnimationTrackType::Opacity;
FName DisplayName;
TArray<FMovieSceneFloatChannel> Channels; // Float→1, Vector2D→2, Color→4
};
C++ Structs (not USTRUCT)
FBakedAnimation
The complete bake result. Not a USTRUCT (contains TUniquePtr), C++ layer only.
struct FBakedAnimation
{
FString SourceText;
int32 GlyphCount = 0;
uint64 TextFingerprint = 0; // CityHash64(SourceText)
uint64 BlueprintFingerprint = 0; // hash(path + asset signatures + BP variables + CompileCount + generated class address)
TUniquePtr<FBakedStage> IntroStage; // may be nullptr
TUniquePtr<FBakedStage> DefaultStage; // usually valid
TUniquePtr<FBakedStage> OutroStage; // may be nullptr
const FBakedStage* GetStage(EAnimationStage Stage) const;
bool HasStage(EAnimationStage Stage) const;
EAnimationStage GetFirstValidStage() const; // Intro first, then Default
EAnimationStage GetNextStageAfter(EAnimationStage Current) const;
float GetTotalDuration() const; // sum of all present stage durations
};
FGlyphCurveSet
Internal C++ temporary computation container — 21 FRichCurves (FRichCurve is not a USTRUCT and cannot be a 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
The two-level tree nodes of rich text parsing (pure C++).
struct TEXTURGE_API FTagNode
{
FString TagName; // empty for the root
TMap<FString, FString> Attributes;
TArray<TSharedPtr<FTagNode>> Children;
TWeakPtr<FTagNode> Parent; // weak pointer prevents cycles
int32 RangeBegin; // plain text start index (inclusive)
int32 RangeEnd; // plain text end index (exclusive)
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 index
FString Text; // TextContent: leaf text
};
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-Channel Mapping Table
| # | Curve field | Track type | State property |
|---|---|---|---|
| 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 (tint share) |
| 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
FGlyphAnimationStatehas 10 fields (not 21) — 21 refers to the curve channel count; Vector2D and Color properties expand into 2 / 4 channels each. New channels are driven by the 21-row member-pointer registry inGlyphChannelTable.h(static_assert(21)); adding a channel requires changes in about 5 places.
Data Structure Relationship Diagram
UTextAnimationBlueprint
├── GlyphAnimations: TArray<UGlyphAnimation*> ← Sequencer curve assets
│ └── MovieScene → UMovieSceneGlyphAnimationTrack
│ └── UMovieSceneGlyphAnimationSection[] (sub-tracks)
│ └── FTextAnimationSubTrackData.Channels (FMovieSceneFloatChannel)
├── StageTransitionConfig
└── [Compile] → UTextAnimationBlueprintGeneratedClass
└── FAnimationCompiler::BakeAllStages(BP, Text, Customizer)
├── ExtractCurves → FGlyphCurveSet (21 FRichCurves, cached)
├── BuildScheduleInfos → TArray<FGlyphScheduleInfo>
└── UGlyphAnimationLayer / UGlyphAnimationFactory
└── Bake() → FBakedStage
└── FBakedAnimation
├── IntroStage / DefaultStage / OutroStage → FBakedStage
│ └── Glyphs: TArray<FBakedGlyph>
│ └── LayerCurves: TArray<FPerLayerCurves> (21 channels × per layer)
│ └── FactoryOverride: FGlyphCurveOverride
├── TextFingerprint / BlueprintFingerprint (uint64)
UTextAnimationStageController::EvaluateAllGlyphs
└── FBakedGlyph::Evaluate(StageTime, OutState)
└── per-layer FPerLayerCurves::Evaluate(LocalTime, OutState) → combined by BlendMode
└── FGlyphAnimationState
Memory Layout Considerations
FBakedAnimationholds the three stages withTUniquePtr; move semantics support Double-Buffer publish switchingFPerLayerCurvesis the largest struct — 21TArray<FRichCurveKey>s;FRichCurveis built temporarily at evaluation (minor performance cost; future optimization: caching with dirty flags)FGlyphAnimationStateis a compact POD type (10 members, ~64 bytes), suitable for stack allocation and batch processing- The runtime hot path (
EvaluateAllGlyphs→FBakedGlyph::Evaluate) uses stack variables throughout — no heap allocation
TIP
Always use stack-allocated
FGlyphAnimationStatevariables on the hot path; avoid creating temporaryTArraycontainers inside loops for maximum throughput. Watch the temporary curve-building cost ofFPerLayerCurves::Evaluatewith large text + many layers.