编辑器定制化

Texturge 编辑器通过 IDetailCustomizationIPropertyTypeCustomizationFKismetCompilerContext 扩展与模板化参数帮助器实现深度定制。

FAnimatedTextBlockDetails

UAnimatedTextBlock 控件提供自定义细节(Details)面板(IDetailCustomization)。

class FAnimatedTextBlockDetails : public IDetailCustomization
{
    static TSharedRef<IDetailCustomization> MakeInstance();
    void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;

private:
    void BuildParameterRows(IDetailCategoryBuilder& AnimCat,
                            IDetailLayoutBuilder& DetailBuilder,
                            UAnimatedTextBlock* Widget,
                            TSharedPtr<IPropertyHandle> OverridesHandle);
    void SaveAllCDODefaults(UTextAnimInstance* CDO);
    UTextAnimInstance* GetTargetCDO() const;
    static bool IsUserParameter(FProperty* Prop);

    TWeakObjectPtr<UAnimatedTextBlock> TargetWidget;
    TMap<FName, TArray<uint8>> CDODefaultsForReset;
};

参数行生成机制

  1. 通过 AddExternalObjectPropertyUAnimParameterOverrides 的 6 组覆盖 Map 作为外部对象属性挂入细节面板
  2. 枚举关联 UTextAnimationBlueprint 生成的 UTextAnimInstance CDO 上的全部属性,筛出 IsUserParameterCPF_BlueprintVisible 且非系统属性)的蓝图变量
  3. 为每个蓝图变量生成一行:变量名 + 覆盖值输入框 + 重置(Reset)按钮
  4. SaveAllCDODefaults 在面板打开时缓存 CDO 默认值,Reset 通过 AnimParamReset<UAnimParameterOverrides> 模板移除覆盖项并回写 CDO 副本

NOTE

CDO 副本方案:面板编辑的是 UTextAnimInstance CDO 的副本,引擎原生编辑器行为,蓝图 CDO 永不修改。烘焙时经 FAnimInstanceCustomizer 注入烘焙临时实例。

FTextAnimationInstanceDetails

UTextAnimInstance 提供自定义细节(Details)面板(IDetailCustomization),仅控制分类排序,不修改属性布局。

class FTextAnimationInstanceDetails : public IDetailCustomization
{
    static TSharedRef<IDetailCustomization> MakeInstance();
    void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
};

FAnimationEntryDetails

FAnimationEntry 结构体提供自定义 Details(IPropertyTypeCustomization),在 UTextAnimationDataAssetEntries 数组中生效。

class FAnimationEntryDetails : public IPropertyTypeCustomization
{
    static TSharedRef<IPropertyTypeCustomization> MakeInstance();
    void CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle,
                         FDetailWidgetRow& HeaderRow,
                         IPropertyTypeCustomizationUtils& CustomizationUtils) override;
    void CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle,
                           IDetailChildrenBuilder& ChildBuilder,
                           IPropertyTypeCustomizationUtils& CustomizationUtils) override;

private:
    TMap<FName, TArray<uint8>> CDODefaults;
};

与控件面板相同,条目级参数覆盖(6 组 Map)也通过 CDO 副本 + Reset 机制编辑,底层复用 TAnimParamTraits<T, FAnimationEntry> 特化。

UTextAnimationTrackProxy

细节面板编辑轨道属性的代理对象(UObject),将 FTextAnimationTrack 包装为可编辑属性:

UCLASS(meta = (DisplayName = "文本动画轨道代理"))
class UTextAnimationTrackProxy : public UObject
{
    UPROPERTY(EditAnywhere, Category = "轨道", meta = (DisplayName = "轨道属性"))
    FTextAnimationTrack Track;
};

FTextAnimationBlueprintCompilerContext

Kismet 编译器扩展,负责将 UTextAnimationBlueprint 编译为 UTextAnimationBlueprintGeneratedClass

class FTextAnimationBlueprintCompilerContext : public FKismetCompilerContext
{
    // FKismetCompilerContext 覆写
    virtual void SpawnNewClass(const FString& NewClassName) override;
    virtual void CleanAndSanitizeClass(UBlueprintGeneratedClass* ClassToClean, UObject*& InOutOldCDO) override;
    virtual void EnsureProperGeneratedClass(UClass*& InOutTargetClass) override;
    virtual void CreateClassVariablesFromBlueprint() override;
    virtual void CreateFunctionList() override;
    virtual void FinishCompilingClass(UClass* Class) override;
    virtual bool ValidateGeneratedClass(UBlueprintGeneratedClass* Class) override;
    virtual void PreCompile() override;

private:
    void ValidateTrackData();   // 轨道数据完整性校验(5 条规则)
    FGuid FixupVariableGUIDs(UTextAnimationBlueprint*, const FName&);
    void CopyAssetDataToClass(UTextAnimationBlueprintGeneratedClass* OutClass);
};

编译流水线

步骤覆写行为
1PreCompile编译前准备
2CreateClassVariablesFromBlueprint基类扫描前确保注入变量(2026-07 重构修正:变量注入提前到基类调用之前)
3FixupVariableGUIDs为蓝图变量维护稳定 GUID(查 VariableNameToGuidMap,未找到则按路径名生成确定性 GUID 并写回)
4ValidateTrackData校验 Tracks 数组完整性(5 条规则)
5FinishCompilingClassCopyAssetDataToClass 复制 Tracks 等资产数据,计算 CompiledTotalDuration = max(StartTime + Duration)
6CompileCount每次编译递增,参与蓝图指纹哈希,保证编译后缓存失效

编译输出写入 UTextAnimationBlueprintGeneratedClassCompiledTracks / CompiledFrameRate / CompiledTotalDuration / CompiledCharacterInterval)。

模板化参数帮助器

AnimParamTraitsHelpers.h(Editor 模块,header-only)提供 3 个共享模板函数:

模板说明
AnimParamNeedsReset<Owner>(Ovr, PropName, EAnimParamType, DefVal)判断当前覆盖值是否偏离 CDO 默认值(决定重置按钮显示)
AnimParamReset<Owner>(Ovr, CDOCopy, PropName, Prop, EAnimParamType, DefVal)移除覆盖项并恢复 CDO 副本默认值
AnimParamSync<Owner>(Ovr, CDOCopy, PropName, EAnimParamType)将 CDO 副本属性值同步写入覆盖 Map

底层映射由运行时模块的 TAnimParamTraits<T, Owner> 特化(AnimatedTextBlock.hDECLARE_ANIM_PARAM_TRAITAnimationEntry.hDECLARE_ENTRY_TRAIT)提供。

扩展自定义 Detail 面板

插件支持注册外部自定义细节面板,步骤:

  1. 创建继承 IDetailCustomization 的类,实现 CustomizeDetails(IDetailLayoutBuilder&)
  2. 在模块的 StartupModule() 中注册:
FPropertyEditorModule& PropertyModule =
    FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomClassLayout(
    UYourClass::StaticClass()->GetFName(),
    FOnGetDetailCustomizationInstance::CreateStatic(&FYourDetails::MakeInstance));
  1. ShutdownModule() 中调用 UnregisterCustomClassLayout 取消注册

所有内置细节自定义均通过相同机制绑定,确保与引擎细节面板标准化。

images/editor-customization.png — 细节面板自定义总览:左侧为 UAnimatedTextBlock 的属性面板(含文本动画蓝图、实例参数覆盖与每个蓝图变量的覆盖输入框和重置按钮),右侧为 UTextAnimationDataAsset 的 Entries 数组编辑(每条 FAnimationEntry 含 TagName、动画蓝图引用与 6 组参数覆盖) — Detail panel customization overview: left shows UAnimatedTextBlock property panel (TextAnimationBlueprint, ParameterOverrides with per-variable override inputs and Reset buttons), right shows UTextAnimationDataAsset Entries array editor (each FAnimationEntry with TagName, blueprint reference and six parameter override maps)