UE5源码分析之Editor——AnimationSettings模块全面分析

AnimationSettings 模块全面系统分析


源文档:https://gitee.com/chouchouxia/share-ue5

目录

  1. 模块概述
  2. 模块整体架构解析
  3. 核心类代码详解
  4. 数据流与运行时行为分析
  5. 功能使用示例编写
  6. 总结与最佳实践

1 模块概述

1.1 基本信息

属性
模块名称 AnimationSettings
类型 Editor(编辑器模块)
位置 Engine/Source/Editor/AnimationSettings/
描述 为动画系统提供编辑器侧的配置监听与自动维护能力

这个模块的体量非常小------仅有一个 .h 文件、一个 .cpp 文件和一个 .Build.cs 文件。代码量不足 100 行,但它在 UE 动画编辑器工作流中扮演了关键的"守护者"角色。

AnimationSettingsModule.h 中只定义了一个类:FAnimationSettingsModule。该模块依赖了 CoreCoreUObjectAssetRegistryEngineSharedSettingsWidgets 共 5 个模块。核心依赖是:IAssetRegistry(资产注册表)、UAnimationSettings(动画全局配置)、UUserDefinedStruct(用户自定义结构体)。

1.2 这个模块到底做了什么?

很多人在刚接触 AnimationSettings 模块时可能会感到困惑------它不像 Persona 那样提供编辑界面,也不像 AnimGraph 那样负责动画蓝图节点,那它存在的意义到底是什么?

答案可以概括为一句话:它负责监听编辑器中资产的重命名操作,并自动把 UAnimationSettings 里的 UserDefinedStructAttributes 引用同步更新到最新名称。

具体来说:

  • 当美术或策划在内容浏览器中重命名 一个 UUserDefinedStruct(用户自定义结构体)时,如果该结构体被配置在"项目设置 → 动画 → 用户自定义结构体动画属性"列表中,FAnimationSettingsModule 会自动感知到这次重命名,并刷新 UAnimationSettings 中对应的软引用,然后把变更写回 DefaultEngine.ini 配置文件。

这就避免了一个非常尴尬的场景:重命名了一个自定义结构体资产后,项目设置里的引用变成了悬空指针,导致动画属性注册静默失败,排查起来费时费力。


2 模块整体架构解析

虽然这个模块非常精简,但它的架构设计仍然值得拆解。我们可以从几个维度来理解它。

2.1 架构图

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                         编辑器运行时 (Editor Runtime)                  │
│                                                                       │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │               内容浏览器 (Content Browser)                      │   │
│  │  用户右击资产 → 重命名 → 触发 FAssetData 变更                   │   │
│  └──────────────────────────┬───────────────────────────────────┘   │
│                             │                                        │
│                             ▼                                        │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │                   IAssetRegistry (资产注册表)                    │   │
│  │  OnAssetRenamed 委托广播                                         │   │
│  └──────────────────────────┬───────────────────────────────────┘   │
│                             │                                        │
│                             ▼                                        │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │               FAnimationSettingsModule (核心模块)               │   │
│  │  ┌────────────────────────────────────────────────────────┐   │   │
│  │  │  FAnimationSettingsModule::OnAssetRenamed()             │   │   │
│  │  │  - 判断资产是否为 UUserDefinedStruct                     │   │   │
│  │  │  - 遍历 UAnimationSettings::UserDefinedStructAttributes  │   │   │
│  │  │  - 更新匹配的软引用                                      │   │   │
│  │  │  - 处理源控签出                                           │   │   │
│  │  │  - 调用 TryUpdateDefaultConfigFile() 写回 ini 文件        │   │   │
│  │  └────────────────────────────────────────────────────────┘   │   │
│  └──────────────────────────┬───────────────────────────────────┘   │
│                             │                                        │
│                             ▼                                        │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │                UAnimationSettings (DeveloperSettings)          │   │
│  │  - UserDefinedStructAttributes: TArray<TSoftObjectPtr<...>>   │   │
│  │  - 持久化到 DefaultEngine.ini                                  │   │
│  │  - 运行时用于注册非可混合动画属性类型                            │   │
│  └──────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘

2.2 模块间依赖关系

如果上面的架构图看起来还有些抽象,可以参考下面这张依赖关系图:

复制代码
FAnimationSettingsModule (监听器)
    │
    ├──→ IAssetRegistry (事件源)
    │       └── OnAssetRenamed 多播委托
    │
    ├──→ UAnimationSettings (配置目标)
    │       └── UserDefinedStructAttributes
    │
    ├──→ UUserDefinedStruct (资产类型判断)
    │
    └──→ SettingsHelpers (源控与文件操作)
            ├── CheckOutOrAddFile()
            └── MakeWritable()

可以看到,FAnimationSettingsModule 本身不包含任何业务逻辑------它是一个纯粹的"桥接"模块,把三个独立系统串在了一起:

  • IAssetRegistry:告诉我们"哪个资产被重命名了"
  • UAnimationSettings:告诉我们"哪些资产需要跟踪引用"
  • SettingsHelpers:告诉我们"怎么安全地把配置写回磁盘"

这种设计的好处是:监听逻辑和业务配置完全解耦。如果以后需要在 UAnimationSettings 中新增更多需要自动维护引用的属性,只需要在 OnAssetRenamed 里追加处理分支即可,框架本身不需要任何改动。

2.3 模块划分

虽然只有一个类,但可以从职责角度进行划分:

1. 事件监听层(StartupModule / ShutdownModule)

  • 职责:在模块启动时向 AssetRegistry 注册回调,在模块关闭时解注册
  • 特点:使用 AddStatic 注册静态回调,避免了对象生命周期管理问题

2. 事件处理层(OnAssetRenamed)

  • 职责:判断重命名的资产类型、查找匹配项、更新引用
  • 特点:纯静态函数,无状态依赖

3. 持久化层(内嵌逻辑)

  • 职责:处理源控签出、使文件可写、写回配置文件
  • 特点:考虑了源控环境、新文件创建等边界情况

2.4 核心技术栈

技术/类 用途
IModuleInterface 模块生命周期管理
IAssetRegistry 资产注册表,提供资产重命名事件
FAssetData 资产元数据包装
UAnimationSettings 继承自 UDeveloperSettings 的全局动画配置
UUserDefinedStruct 用户自定义结构体资产
TSoftObjectPtr 软对象指针,避免强引用导致的内存驻留
FPlatformFileManager 平台文件系统访问
SettingsHelpers 引擎设置辅助工具(源控签出、文件权限)
FDelegateHandle 委托句柄,用于取消注册

3 核心类代码详解

3.1 FAnimationSettingsModule 类

3.1.1 概述

FAnimationSettingsModule 是整个模块唯一的功能类,实现了 IModuleInterface 接口。它的核心职责是当内容浏览器中的 UUserDefinedStruct 资产被重命名时,自动同步 UAnimationSettings 中的引用

核心设计理念:

  • 轻量级:仅两个方法 + 一个静态回调 + 一个成员变量
  • 按需触发:不轮询、不 Tick,完全由事件驱动
  • 无副作用的旁路逻辑:不修改资产本身,只更新配置引用

3.1.2 StartupModule / ShutdownModule

cpp 复制代码
void FAnimationSettingsModule::StartupModule()
{
    const FAssetRegistryModule& AssetRegistryModule =
        FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));

    OnAssetRenamedHandle = AssetRegistryModule.Get().OnAssetRenamed()
        .AddStatic(&FAnimationSettingsModule::OnAssetRenamed);
}

void FAnimationSettingsModule::ShutdownModule()
{
    if (const FAssetRegistryModule* AssetRegistryModule =
        FModuleManager::GetModulePtr<FAssetRegistryModule>(TEXT("AssetRegistry")))
    {
        if (IAssetRegistry* AssetRegistry = AssetRegistryModule->TryGet())
        {
            AssetRegistry->OnAssetRenamed().Remove(OnAssetRenamedHandle);
        }
    }
}

功能分析:

  • StartupModule() 在编辑器启动时被调用,通过 FModuleManager 加载 AssetRegistry 模块,然后使用 AddStatic 将自己注册为资产重命名事件的一个监听者。这里用 AddStatic 而非 AddRawAddSP,是因为 OnAssetRenamed 被声明为 static 成员函数------它不依赖模块实例的状态,解注册时也不需要担心 this 指针失效的问题。

  • ShutdownModule() 是注册的逆操作。注意这里的防御性写法 :先用 GetModulePtr 检查模块指针是否还活着,再用 TryGet 检查 IAssetRegistry 接口是否还可用。之所以这么做,是因为编辑器关闭时模块卸载顺序是不确定的------AssetRegistry 可能比 AnimationSettings 先卸载,如果直接解引用才会崩溃。

  • OnAssetRenamedHandleFDelegateHandle)是模块唯一的成员变量,用于在关闭时精确移除之前绑定的委托。


3.1.3 OnAssetRenamed(核心逻辑)

这是整个模块的灵魂所在。让我们分段来读:

cpp 复制代码
void FAnimationSettingsModule::OnAssetRenamed(const FAssetData& InAssetData, const FString& InOldName)
{
    if (UUserDefinedStruct* UserDefinedStruct = Cast<UUserDefinedStruct>(InAssetData.GetAsset()))
    {

第一步:类型过滤。 通过 Cast 判断被重命名的资产是不是 UUserDefinedStruct。如果不是(比如重命名的是一张贴图、一个蓝图),直接跳过,零开销返回。这里使用 GetAsset() 会触发一次同步加载,但由于用户重命名操作本身就是低频操作,这个开销完全可以接受。

cpp 复制代码
        if (UAnimationSettings* Settings = UAnimationSettings::Get())
        {
            TArray<int32> Indices;
            for (int32 Index = 0; Index < Settings->UserDefinedStructAttributes.Num(); Index++)
            {
                const TSoftObjectPtr<UUserDefinedStruct>& StructPtr =
                    Settings->UserDefinedStructAttributes[Index];
                UUserDefinedStruct* Struct = StructPtr.Get();
                if (Struct == UserDefinedStruct)
                {
                    Indices.Add(Index);
                }
            }

第二步:收集匹配项。 遍历 UAnimationSettings::UserDefinedStructAttributes 数组,找出所有指向"旧身份对象"的索引。这里有一个值得注意的细节------TSoftObjectPtr::Get() 在对象已加载时返回裸指针,而 InAssetData.GetAsset() 已经在前面加载了这个对象,所以这里的 StructPtr.Get()UserDefinedStruct 做指针比较是安全和有效的。

另外注意 Indices 是一个数组而非单个索引------这意味着同一个 UUserDefinedStruct 可能被配置了多次(虽然实际场景中很少见),代码对此做了容错处理。

cpp 复制代码
            if (Indices.Num() > 0)
            {
                for (int32 Index : Indices)
                {
                    Settings->UserDefinedStructAttributes[Index] = UserDefinedStruct;
                }

第三步:更新引用。 直接把旧的 TSoftObjectPtr 替换为指向"新身份对象"的 TSoftObjectPtr。这看起来像是一个无操作------前后对象指针相同,为什么还需要替换?答案在于 TSoftObjectPtr 内部存储的是路径字符串 。资产重命名后,旧路径失效了,需要把 TSoftObjectPtr 的内部路径更新为新路径。直接把新对象赋值给 TSoftObjectPtr,UE 的赋值运算符会自动从对象身上获取最新的路径。

cpp 复制代码
                FString RelativePath;
                bool bIsSourceControlled = false;
                bool bIsNewFile = false;

                check(Settings->GetClass()->HasAnyClassFlags(CLASS_DefaultConfig));

                // Attempt to checkout the file automatically
                RelativePath = Settings->GetDefaultConfigFilename();
                bIsSourceControlled = true;
                FString FullPath = FPaths::ConvertRelativePathToFull(RelativePath);

                if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*FullPath))
                {
                    bIsNewFile = true;
                }

                if (!bIsSourceControlled || !SettingsHelpers::CheckOutOrAddFile(FullPath))
                {
                    SettingsHelpers::MakeWritable(FullPath);
                }

                Settings->TryUpdateDefaultConfigFile();

                if (bIsNewFile && bIsSourceControlled)
                {
                    SettingsHelpers::CheckOutOrAddFile(FullPath);
                }

第四步:持久化配置。 这是最复杂的部分,涉及到配置文件的源控管理:

  1. 定位配置文件 :通过 GetDefaultConfigFilename() 获取 DefaultEngine.ini 的路径(UAnimationSettings 被标记为 config=Engine,所以配置存在引擎的默认配置文件中)。

  2. 检查文件是否存在 :如果是新创建的配置文件(第一次保存),标记为 bIsNewFile

  3. 源控签出 :调用 SettingsHelpers::CheckOutOrAddFile() 尝试从 Perforce/SVN 签出文件。如果签出失败(比如不在源控环境下),则退化为 MakeWritable() 去掉只读属性。

  4. 写回配置 :调用 TryUpdateDefaultConfigFile()UAnimationSettings 的当前内存状态序列化回 .ini 文件。

  5. 二次源控处理 :如果文件是新创建的且在源控环境下,在文件写入后再做一次 CheckOutOrAddFileadd 操作需要在文件存在后才能执行)。

这个流程虽然看似冗长,但它保证了在多人协作的源控环境中,配置文件变更不会被静默丢弃。


3.1.4 设计亮点

  1. 事件驱动,零轮询开销:不在 Tick 里做任何检查,只在真正发生重命名时才触发逻辑
  2. 防御性关闭ShutdownModule 里用了 GetModulePtr + TryGet 两层保护,避免卸载顺序问题
  3. 静态回调OnAssetRenamed 是静态函数,不依赖 this,避免了模块实例已被销毁但回调仍被触发的悬空指针风险
  4. 源控友好:自动处理签出、权限、新文件创建等边界情况

3.2 UAnimationSettings 配置类

虽然 UAnimationSettings 本身定义在 Engine 运行时模块而非 AnimationSettings 编辑器模块中,但它是本模块的"被服务对象",有必要了解其结构。

3.2.1 类层次

复制代码
UObject
  └── UDeveloperSettings
        └── UAnimationSettings

UAnimationSettings 继承自 UDeveloperSettings,这意味着它会自动出现在 UE 编辑器的 Project Settings(项目设置) 面板中。UDeveloperSettings 提供了一个 SectionName 字段用于分组,UAnimationSettings 在构造函数中将其设置为 "Animation",所以配置项在项目设置中归类于 Engine → Animation 分组。

3.2.2 配置属性分类

分类 属性 作用
Compression CompressCommandletVersion 压缩版本号,递增可触发全量重压缩
KeyEndEffectorsMatchNameArray 需要高精度压缩的骨骼名称模式(如 IK、手、武器)
ForceRecompression 是否强制重压缩所有动画
bForceBelowThreshold 是否在误差超过阈值时允许使用更大但更精确的压缩方式
bFirstRecompressUsingCurrentOrDefault 是否优先使用当前压缩器重压缩
Performance bEnablePerformanceLog 是否输出压缩性能日志
bStripAnimationDataOnDedicatedServer 是否在 DS 上剥离动画轨道数据
bTickAnimationOnSkeletalMeshInit 是否在骨骼网格初始化时执行动画 Tick
Animation Attributes BoneTimecodeCustomAttributeNameSettings 时间码动画属性名称配置
BoneCustomAttributesNames 直接导入到骨骼的自定义属性名称列表
BoneNamesWithCustomAttributes 全部动画属性都直接导入的骨骼名称列表
AttributeBlendModes 按名称指定的动画属性混合模式
DefaultAttributeBlendMode 默认动画属性混合模式
TransformAttributeNames FBX 节点变换曲线导入为属性的名称匹配规则
UserDefinedStructAttributes 用户自定义结构体动画属性列表(本模块监听的核心目标)
Mirroring MirrorFindReplaceExpressions 镜像骨骼的查找替换表达式(如 l_r_
Animation Data DefaultFrameRate 项目默认帧率(默认 30fps)
bEnforceSupportedFrameRates 是否强制动画资产只能使用受支持的帧率

3.2.3 UserDefinedStructAttributes 的运行时意义

cpp 复制代码
UPROPERTY(config, EditAnywhere, Category = AnimationAttributes,
    meta=(AllowedClasses="/Script/Engine.UserDefinedStruct"))
TArray<TSoftObjectPtr<UUserDefinedStruct>> UserDefinedStructAttributes;

这个属性允许在项目设置中注册一组 UUserDefinedStruct,使其成为动画系统的非可混合(Non-blendable)属性类型

PostEditChangeProperty 检测到这个数组发生变化时,会调用:

cpp 复制代码
UE::Anim::AttributeTypes::RegisterNonBlendableType(UserDefinedStruct.Get());

将其注册到 FAttributeTypes 全局注册表中。动画蓝图、动画节点等在计算混合时会查询这个注册表------被标记为 Non-blendable 的类型不会参与混合计算,而是取最高权重的值。

这就是为什么 FAnimationSettingsModule 需要监听重命名------如果这个引用断掉了,结构体类型就不会被正确注册,运行时行为会静默出错。


4 数据流与运行时行为分析

4.1 正常流程:资产重命名

复制代码
1. 用户在内容浏览器中右击一个 UUserDefinedStruct 资产,选择"重命名"
   ↓
2. AssetRegistry 处理重命名,更新内部索引
   ↓
3. AssetRegistry 广播 OnAssetRenamed 多播委托
   ↓
4. FAnimationSettingsModule::OnAssetRenamed 被调用
   ├── 4a. Cast<UUserDefinedStruct> ------ 不是目标类型 → 直接返回
   └── 4b. 是目标类型 → 继续
   ↓
5. 获取 UAnimationSettings 单例
   ↓
6. 遍历 UserDefinedStructAttributes 数组,收集匹配索引
   ↓
7. 更新所有匹配的 TSoftObjectPtr 引用
   ↓
8. 定位 DefaultEngine.ini 文件路径
   ↓
9. 源控签出 / 去掉只读属性
   ↓
10. TryUpdateDefaultConfigFile() 写回配置
   ↓
11. 完成 ------ 配置文件中的引用已同步为新名称

4.2 模块加载/卸载流程

复制代码
编辑器启动
    │
    ▼
FAnimationSettingsModule::StartupModule()
    │
    ├─→ 加载 AssetRegistry 模块
    │
    └─→ 注册 OnAssetRenamed 静态回调
            │
            │  ... 编辑器运行中 ...
            │
            ▼
编辑器关闭 / 模块热重载
    │
    ▼
FAnimationSettingsModule::ShutdownModule()
    │
    ├─→ 获取 AssetRegistryModule 指针(防御性检查)
    │
    ├─→ 获取 IAssetRegistry 接口(防御性检查)
    │
    └─→ 移除 OnAssetRenamedHandle 委托

4.3 边界情况处理

场景 处理方式
重命名的资产不是 UUserDefinedStruct Cast 失败,直接返回,零额外开销
UAnimationSettings 获取失败 if 保护,不做任何操作
资产不在 UserDefinedStructAttributes Indices.Num() == 0,外层 if 拦截
配置文件在源控下 CheckOutOrAddFile 自动签出
配置文件是只读的 MakeWritable 去掉只读属性
配置文件是新创建的 先写入再补做 add 到源控
AssetRegistry 模块先于本模块卸载 GetModulePtr 返回空,ShutdownModule 安全跳过

5 功能使用示例编写

示例1:在项目中使用用户自定义结构体作为动画属性

首先,在内容浏览器中创建一个 UserDefinedStruct(比如叫 FMyAnimAttribute),给它添加几个成员变量:

复制代码
内容浏览器 → 右键 → Miscellaneous → User Defined Struct
命名为: FAttackAttribute
成员: float DamageMultiplier, FVector HitDirection, bool bIsCharged

然后在项目设置中注册这个结构体:

复制代码
Edit → Project Settings → Engine → Animation → 
User Defined Struct Animation Attributes → 添加 FAttackAttribute

此时,FAnimationSettingsModule 已经开始工作了------如果后续在内容浏览器中把 FAttackAttribute 重命名为 FCombatAttribute,模块会自动更新项目设置中的引用。

示例2:在动画蓝图中读取自定义属性

cpp 复制代码
// 在 AnimInstance 子类中
void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
    Super::NativeUpdateAnimation(DeltaSeconds);

    // 从动画曲线/属性系统中读取自定义属性
    if (const FAttackAttribute* Attr = GetAttackingAnimAttribute())
    {
        CurrentDamageMultiplier = Attr->DamageMultiplier;
        CurrentHitDirection = Attr->HitDirection;
        bIsChargedAttack = Attr->bIsCharged;
    }
}

示例3:在 C++ 中访问动画设置

cpp 复制代码
// 在编辑器中获取动画全局设置
#if WITH_EDITOR
void UMyAnimationTool::CheckCompressionSettings()
{
    UAnimationSettings* AnimSettings = UAnimationSettings::Get();
    if (AnimSettings)
    {
        UE_LOG(LogTemp, Log, TEXT("当前压缩版本: %d"), AnimSettings->CompressCommandletVersion);
        UE_LOG(LogTemp, Log, TEXT("默认帧率: %.2f"), AnimSettings->GetDefaultFrameRate().AsDecimal());
        UE_LOG(LogTemp, Log, TEXT("高精度骨骼模式数量: %d"), AnimSettings->KeyEndEffectorsMatchNameArray.Num());
    }
}
#endif

示例4:监听动画设置变更(编辑器工具开发)

cpp 复制代码
// 在编辑器工具中监听 UAnimationSettings 的变更
#if WITH_EDITOR
void UMyEditorTool::Initialize()
{
    UAnimationSettings* Settings = UAnimationSettings::Get();
    if (Settings)
    {
        Settings->OnSettingChanged().AddUObject(this, &UMyEditorTool::OnAnimationSettingsChanged);
    }
}

void UMyEditorTool::OnAnimationSettingsChanged(UObject* Settings, const FPropertyChangedEvent& Event)
{
    const FName PropertyName = Event.GetPropertyName();
    if (PropertyName == GET_MEMBER_NAME_CHECKED(UAnimationSettings, DefaultFrameRate))
    {
        // 默认帧率变更,刷新相关 UI 或重新计算
        RefreshFrameRateDependentData();
    }
}
#endif

6 总结与最佳实践

6.1 核心要点

  1. 定位清晰,职责单一

    • FAnimationSettingsModule 不做动画计算、不提供 UI,只做一件事:保持 UAnimationSettings::UserDefinedStructAttributes 的引用有效性
    • 这种"小而精"的模块设计是 UE 编辑器的惯用模式------每个模块解决一个明确的痛点
  2. 事件驱动优于轮询

    • 通过 IAssetRegistry::OnAssetRenamed 委托获取精确的变更通知
    • 不需要在 Tick 中遍历所有资产做 diff 对比
    • 零 CPU 开销,只在真正发生重命名时才执行
  3. 源控友好是编辑器模块的基本素养

    • 自动处理 Perforce/SVN 签出
    • 降级处理只读文件
    • 正确处理新文件创建场景
  4. 防御性编程贯穿始终

    • StartupModule 使用 LoadModuleChecked(假设 AssetRegistry 一定存在)
    • ShutdownModule 使用 GetModulePtr + TryGet(假设它可能已经卸载)
    • 所有指针访问都有有效性检查

6.2 最佳实践

  1. 如果需要在项目设置中新增类似"资产引用"配置项

    • 可以参考 FAnimationSettingsModule 的模式:写一个轻量编辑器模块,监听 AssetRegistryOnAssetRenamed 事件
    • 在回调中遍历需要维护的引用数组,更新匹配的 TSoftObjectPtr
    • 务必处理配置文件写回和源控签出
  2. 开发编辑器工具时的模块加载/卸载规范

    • StartupModule 中注册的委托,必须在 ShutdownModule 中显式解注册
    • 解注册时考虑模块卸载顺序,用防御式写法
    • 静态回调比成员函数回调更安全(不依赖 this 生命周期)
  3. UDeveloperSettings 的使用建议

    • 全局配置类继承 UDeveloperSettings,可以自动在项目设置面板中展示
    • 在构造函数中设置 SectionName 来决定分类
    • 需要持久化引用的属性使用 TSoftObjectPtr 而非硬引用,避免强依赖
  4. 理解 UAnimationSettings 各属性的影响范围

    • CompressCommandletVersion:递增后会触发命令行重压缩所有动画
    • bStripAnimationDataOnDedicatedServer:打开后 DS 不包含动画数据,可显著减少 DS 内存和包体大小
    • DefaultFrameRate:影响所有新建动画资产的默认帧率

6.3 常见问题

问题1:重命名了结构体后,动画属性注册失效

  • 确认 AnimationSettings 模块是否正常加载(检查日志是否有相关报错)
  • 手动检查 DefaultEngine.ini[/Script/Engine.AnimationSettings] 段落的 UserDefinedStructAttributes 路径是否正确
  • 如果配置文件处于源控锁定状态且签出失败,引用不会更新

问题2:哪些操作会触发 OnAssetRenamed?

  • 内容浏览器中直接重命名
  • 通过资产工具批量重命名
  • 蓝图编译触发的重定向器处理也可能间接触发
  • 注意:移动资产(Move/Rename 同时发生)也会触发,且行为相同

问题3:模块似乎没有对重定向器(Redirector)做特殊处理?

  • 实际上,InAssetData.GetAsset() 会自动遵循重定向器加载目标资产,所以不需要显式处理

附录:文件结构参考

复制代码
AnimationSettings/
├── AnimationSettings.Build.cs        # 模块构建配置
├── Public/
│   └── AnimationSettingsModule.h     # 模块接口声明
└── Private/
    └── AnimationSettingsModule.cpp   # 模块实现

相关文件

文件 路径
UAnimationSettings 声明 Engine/Source/Runtime/Engine/Classes/Animation/AnimationSettings.h
UAnimationSettings 实现 Engine/Source/Runtime/Engine/Private/Animation/AnimationSettings.cpp
CustomAttributes Engine/Source/Runtime/Engine/Classes/Animation/CustomAttributes.h
AttributeTypes Engine/Source/Runtime/Engine/Public/Animation/AttributeTypes.h
相关推荐
远离UE420 小时前
UE5 SF_VertexShader 如何使用
ue5
电子云与长程纠缠1 天前
UE5 Lyra PocketWorld进行3D内容UI预览 - 上
开发语言·学习·3d·ue5·游戏引擎
电子云与长程纠缠1 天前
UE5 Lyra PocketWorld进行3D内容UI预览 - 下
开发语言·学习·游戏·ui·ue5
远离UE42 天前
UE5 GPU内部异步
ue5
1204157137 肖哥2 天前
UE5 PCG PCGBlueprintElement简介
ue5
dong1326972 天前
UE5FPS游戏开发教程(四)
ue5
dong1326973 天前
UE5FPS游戏开发教程(三)
ue5
日月云棠3 天前
UE5源码分析之Editor——AnimationBlueprintLibrary模块全面分析
ue5
远离UE43 天前
UE5 ECS 原理
ue5