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

AnimationEditor 模块全面系统分析


目录

  1. 模块概述
  2. 模块整体架构解析
  3. 类级代码注释详解
  4. 功能使用示例编写
  5. [如何扩展 AnimationEditor](#如何扩展 AnimationEditor)
  6. 总结与最佳实践

### 文章目录

  • [AnimationEditor 模块全面系统分析](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [目录](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [@toc](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [1 模块概述](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [1.1 基本信息](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [1.2 为什么需要 AnimationEditor 模块?](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [2 模块整体架构解析](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [2.1 架构图](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [2.2 模块间依赖关系](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [2.3 模块划分](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [2.4 数据流走向](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [2.5 核心技术栈](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [2.6 架构设计优势](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [潜在改进点](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3 类级代码注释详解](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.1 IAnimationEditorModule 接口](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.2 IAnimationEditor 接口](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.3 FAnimationEditor 类](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.3.1 初始化与生命周期](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.3.2 Tick 机制](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.3.3 命令绑定](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.3.4 动画操作](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.3.5 编辑器刷新](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.4 FAnimationEditorMode 类](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.5 FAnimationEditorCommands 类](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.6 UAnimationToolMenuContext 类](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [3.7 FAnimationEditorModule 类](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [4 功能使用示例编写](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [示例1:自定义动画编辑器工具栏扩展](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [示例2:通过资产工具打开动画编辑器](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [示例3:监听动画编辑器事件](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [5 如何扩展 AnimationEditor](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [5.1 扩展工具栏](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [5.2 扩展菜单](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [5.3 注册自定义面板](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [5.4 扩展布局](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [6 总结与最佳实践](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [核心要点](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [最佳实践](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [常见问题解决](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [附录](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [A. 相关资源](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)
  • [B. 文件结构参考](#文章目录 AnimationEditor 模块全面系统分析 目录 @[toc] 1 模块概述 1.1 基本信息 1.2 为什么需要 AnimationEditor 模块? 2 模块整体架构解析 2.1 架构图 2.2 模块间依赖关系 2.3 模块划分 2.4 数据流走向 2.5 核心技术栈 2.6 架构设计优势 潜在改进点 3 类级代码注释详解 3.1 IAnimationEditorModule 接口 3.2 IAnimationEditor 接口 3.3 FAnimationEditor 类 3.3.1 初始化与生命周期 3.3.2 Tick 机制 3.3.3 命令绑定 3.3.4 动画操作 3.3.5 编辑器刷新 3.4 FAnimationEditorMode 类 3.5 FAnimationEditorCommands 类 3.6 UAnimationToolMenuContext 类 3.7 FAnimationEditorModule 类 4 功能使用示例编写 示例1:自定义动画编辑器工具栏扩展 示例2:通过资产工具打开动画编辑器 示例3:监听动画编辑器事件 5 如何扩展 AnimationEditor 5.1 扩展工具栏 5.2 扩展菜单 5.3 注册自定义面板 5.4 扩展布局 6 总结与最佳实践 核心要点 最佳实践 常见问题解决 附录 A. 相关资源 B. 文件结构参考)

1 模块概述

1.1 基本信息

属性
模块名称 AnimationEditor
类型 Editor(编辑器模块)
位置 Engine/Source/Editor/AnimationEditor
描述 UE5 动画资产编辑器,提供动画序列(AnimSequence)、动画蒙太奇(AnimMontage)等动画资产的编辑功能

整个模块共包含 8 个源文件(4 个公开头文件 + 4 个私有实现文件),代码量大约 900 行。这个模块是 UE5 编辑器工具链中 Persona 动画系统的一个核心组成部分,负责为 UAnimationAsset 及其子类(UAnimSequenceUAnimMontageUAnimCompositeBase 等)提供完整的可视化编辑环境。

在 AnimationEditor 模块中,主要定义了 6 个关键类型:IAnimationEditor(编辑器接口)、IAnimationEditorModule(模块接口)、FAnimationEditor(编辑器实现)、FAnimationEditorMode(应用模式)、FAnimationEditorCommands(命令定义)、UAnimationToolMenuContext(菜单上下文)。

这个模块主要依赖了引擎的 Core、CoreUObject、Engine、Slate、Persona、SkeletonEditor、AnimGraph、ToolMenus 等模块。使用到的核心依赖是:Persona(动画工具集框架)、SkeletonEditor(骨骼树编辑)、AnimGraph(动画蓝图图表)、WorkflowOrientedApp(工作流应用框架)。


1.2 为什么需要 AnimationEditor 模块?

很多朋友可能觉得奇怪------UE 本身不是已经有 Persona 编辑器了吗?为什么要单独抽一个 AnimationEditor 模块出来?

其实,Persona 是一个编辑器框架 ,它提供了一套通用的骨骼网格、动画、物理资产的编辑基础设施。而 AnimationEditor 是 Persona 框架下的一个具体应用,专门负责动画序列和动画蒙太奇的编辑。如果没有这个模块:

  • 每次打开一个 UAnimSequence 资产,Persona 框架不知道应该展示哪些面板、布局长什么样
  • 动画资产的压缩、重导入、FBX 导出等操作没有统一的入口
  • 曲线编辑、动画曲线查看器、蒙太奇分段编辑等动画专属功能无法集成到编辑器里
  • 外部模块无法扩展动画编辑器的工具栏和菜单

AnimationEditor 模块帮我们解决了"在 Persona 框架下,如何为动画资产构建一个完整、可用、可扩展的编辑环境"这个问题。


2 模块整体架构解析

2.1 架构图

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                     外部调用者 (External Callers)                      │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────────┐  │
│  │  Asset Tools │  │  Other Editors│  │  External Extensions    │  │
│  │  (双击资产)  │  │  (Skeleton等) │  │  (工具栏/菜单扩展)      │  │
│  └──────┬───────┘  └──────┬───────┘  └──────────┬───────────────┘  │
└─────────┼──────────────────┼──────────────────────┼──────────────────┘
          │                  │                      │
          ▼                  ▼                      ▼
┌─────────────────────────────────────────────────────────────────────┐
│                 IAnimationEditorModule (模块接口)                      │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │  - CreateAnimationEditor()         创建编辑器实例               │ │
│  │  - GetMenuExtensibilityManager()   获取菜单扩展管理器           │ │
│  │  - GetToolBarExtensibilityManager()获取工具栏扩展管理器         │ │
│  │  - GetAllAnimationEditorToolbarExtenders() 获取所有工具栏扩展器 │ │
│  └────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              │ 创建
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    IAnimationEditor (编辑器接口)                       │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │  继承: FPersonaAssetEditorToolkit + IHasPersonaToolkit          │ │
│  │  核心API:                                                       │ │
│  │  - SetAnimationAsset()         设置当前编辑的动画资产           │ │
│  │  - GetAssetBrowser()           获取资产浏览器                   │ │
│  │  - EditCurves()                编辑曲线                         │ │
│  │  - StopEditingCurves()         停止编辑曲线                     │ │
│  └────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              │ 实现
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                      FAnimationEditor (编辑器实现)                     │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │  继承: IAnimationEditor, FGCObject, FTickableEditorObject       │ │
│  │  核心成员:                                                       │ │
│  │  - AnimationAsset           当前编辑的动画资产                  │ │
│  │  - PersonaToolkit           Persona 工具集                      │ │
│  │  - SkeletonTree             骨骼树                              │ │
│  │  - DetailsView              细节面板                            │ │
│  │  - SequenceBrowser          序列浏览器                          │ │
│  │  - CurveEditor              曲线编辑器                          │ │
│  │  核心功能:                                                       │ │
│  │  - InitAnimationEditor()    初始化编辑器                       │ │
│  │  - ExtendMenu()/ExtendToolbar() 扩展菜单/工具栏                │ │
│  │  - BindCommands()           绑定UI命令                          │ │
│  │  - OpenNewAnimationDocumentTab() 打开文档标签页                │ │
│  └────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
          │                               │
          │ 使用                          │ 使用
          ▼                               ▼
┌─────────────────────┐    ┌─────────────────────────────┐
│  FAnimationEditorMode│    │  FAnimationEditorCommands   │
│  (应用模式)          │    │  (命令定义)                  │
├─────────────────────┤    ├─────────────────────────────┤
│  - TabFactories      │    │  - ImportAnimation          │
│  - TabLayout         │    │  - ReimportAnimation        │
│  - RegisterTab-      │    │  - ApplyCompression         │
│    Factories()       │    │  - SetKey                   │
│  - 定义面板布局      │    │  - ExportToFBX_*            │
└─────────────────────┘    │  - AddLoopingInterpolation  │
                           │  - RemoveBoneTracks         │
                           └─────────────────────────────┘

2.2 模块间依赖关系

如果上述架构图看起来还有点抽象,可以参考下面的依赖关系进行理解。

复制代码
IAnimationEditorModule (模块入口)
    ↓ 创建
IAnimationEditor (接口)
    ↓ 实现
FAnimationEditor (编辑器主体)
    ├─→ FAnimationEditorMode (定义应用模式、面板布局)
    │       ├─→ ISkeletonEditorModule (创建骨骼树Tab)
    │       └─→ FPersonaModule (创建Details、Viewport、AssetBrowser等Tab)
    ├─→ FAnimationEditorCommands (注册UI命令)
    ├─→ IPersonaToolkit (Persona工具集)
    │       ├─→ IPersonaPreviewScene (预览场景)
    │       └─→ USkeleton (骨骼)
    ├─→ ISkeletonTree (骨骼树)
    └─→ UAnimationToolMenuContext (菜单上下文)

可能大家看到这里会感到有些疑惑------为什么 AnimationEditor 模块自己不实现所有的面板,而是大量依赖 Persona 模块来创建?

其实,这正是 UE5 编辑器框架设计的精妙之处。AnimationEditor 的职责是组装 ,而不是实现。Persona 模块提供了一套通用的、可复用的面板工厂(TabFactory),比如细节面板、视口面板、资产浏览器面板等。AnimationEditor 模块只需要:

  1. 决定使用哪些面板(通过 FAnimationEditorMode 注册)
  2. 决定面板怎么排列(通过 TabLayout 定义布局)
  3. 决定面板之间如何交互(通过回调委托连接)

这种设计让 AnimationEditor 的代码量保持在一个很小的规模(约 900 行),同时可以复用 Persona 模块中经过充分测试的复杂面板逻辑。如果将来需要为 SkeletonEditor、PhysicsAssetEditor 等编辑器添加动画编辑能力,它们也可以用同样的方式复用这些面板。

2.3 模块划分

对于每个模块的功能总结如下:

1. IAnimationEditorModule - 模块接口层

职责:

  • 提供模块的入口点
  • 创建编辑器实例
  • 管理菜单和工具栏的扩展性

特点:

  • 继承 IModuleInterfaceIHasMenuExtensibilityIHasToolBarExtensibility
  • 通过 FExtensibilityManager 提供扩展点,允许外部模块注入菜单项和工具栏按钮
  • 额外提供 FAnimationEditorToolbarExtender 委托,让外部模块可以添加自定义工具栏扩展

2. IAnimationEditor - 编辑器接口层

职责:

  • 定义动画编辑器的公共 API
  • 提供曲线编辑的支持结构

特点:

  • 继承 FPersonaAssetEditorToolkit(Persona 资产编辑器工具集基类)和 IHasPersonaToolkit
  • 定义了 FCurveEditInfo 结构体,用于描述曲线编辑信息
  • 接口设计简洁,只暴露必要的方法

3. FAnimationEditor - 编辑器实现层

职责:

  • 实现 IAnimationEditor 接口
  • 管理编辑器的生命周期
  • 处理用户交互(压缩、重导入、导出等)
  • 协调各个面板之间的数据流动

特点:

  • 同时继承 FGCObject(垃圾回收)和 FTickableEditorObject(每帧 Tick)
  • 管理 Persona 工具集、骨骼树、序列浏览器、曲线编辑器等核心组件
  • 提供丰富的动画操作:压缩、导出 FBX、添加循环插值、移除骨骼轨道、复制曲线到 SoundWave 等

4. FAnimationEditorMode - 应用模式层

职责:

  • 定义编辑器的面板布局(TabLayout)
  • 注册所有面板工厂(TabFactory)

特点:

  • 继承 FApplicationMode
  • 定义了完整的编辑器布局:左侧骨骼树/资产详情 → 中间视口/文档 → 右侧细节面板/资产浏览器
  • 通过 PersonaModule.OnRegisterTabs()PersonaModule.OnRegisterLayoutExtensions() 广播支持外部扩展

5. FAnimationEditorCommands - 命令定义层

职责:

  • 定义编辑器中所有可用的 UI 命令

特点:

  • 继承 TCommands<FAnimationEditorCommands>
  • 使用 UI_COMMAND 宏定义命令,包括名称、描述、快捷键
  • 涵盖导入、重导入、压缩、FBX 导出、循环插值、关键帧、移除骨骼轨道等 9 个命令

6. UAnimationToolMenuContext - 菜单上下文层

职责:

  • 在工具菜单中传递编辑器实例的引用

特点:

  • 继承 UObject
  • 持有 TWeakPtr<IAnimationEditor> 弱引用
  • 通过 FToolMenuContext 在菜单回调中获取编辑器实例

2.4 数据流走向

编辑器初始化流程:

复制代码
1. 用户双击动画资产(UAnimSequence/UAnimMontage 等)
   ↓
2. AssetTools 调用 IAnimationEditorModule::CreateAnimationEditor()
   ↓
3. FAnimationEditorModule 创建 FAnimationEditor 实例
   ↓
4. FAnimationEditor::InitAnimationEditor() 被调用
   ├─→ 注册重导入/导入回调(FReimportManager + UImportSubsystem)
   ├─→ 创建 IPersonaToolkit(通过 FPersonaModule)
   ├─→ 创建 ISkeletonTree(通过 ISkeletonEditorModule)
   ├─→ 调用 FAssetEditorToolkit::InitAssetEditor() 初始化编辑器框架
   ├─→ BindCommands() 绑定 UI 命令
   ├─→ 创建 FAnimationEditorMode 并设为当前模式
   ├─→ ExtendMenu() / ExtendToolbar() 扩展菜单和工具栏
   └─→ OpenNewAnimationDocumentTab() 打开动画文档标签页

切换动画资产流程:

复制代码
1. 用户在资产浏览器中选中另一个动画资产
   ↓
2. HandleOpenNewAsset() 被调用
   ↓
3. OpenNewAnimationDocumentTab(NewAsset) 被调用
   ├─→ 通过 PersonaModule 创建新的文档 Widget
   ├─→ RemoveEditingObject(旧资产) + AddEditingObject(新资产)
   ├─→ 更新 PersonaToolkit 的预览动画资产
   ├─→ 关闭旧的曲线编辑标签页(如果存在)
   ├─→ 如果是 AnimMontage,激活蒙太奇分段标签页
   └─→ 通知 AssetFamily 记录资产打开

动画操作流程(以压缩为例):

复制代码
1. 用户点击工具栏 "Apply Compression" 按钮
   ↓
2. FAnimationEditorCommands::ApplyCompression 命令触发
   ↓
3. FAnimationEditor::OnApplyCompression() 被调用
   ├─→ 检查当前资产是否为 UAnimSequence
   ├─→ 收集 AnimSequences 数组
   └─→ 调用 FPersonaModule::ApplyCompression() 执行压缩

2.5 核心技术栈

技术/类 用途
FPersonaAssetEditorToolkit Persona 资产编辑器工具集基类
IPersonaToolkit Persona 工具集接口,管理预览场景、骨骼等
ISkeletonTree 骨骼树接口,显示骨骼层级结构
IAnimationSequenceBrowser 动画序列浏览器,浏览和管理动画资产
IAnimSequenceCurveEditor 动画序列曲线编辑器
FWorkflowCentricApplication 工作流中心应用,管理应用模式和 Tab
FApplicationMode 应用模式,定义布局和 Tab 工厂
FExtensibilityManager 扩展性管理器,支持菜单/工具栏扩展
UToolMenus 工具菜单系统(UE5 新菜单框架)
FReimportManager 重导入管理器
FAssetEditorToolkit 资产编辑器工具集基类
FTickableEditorObject 可 Tick 的编辑器对象
FGCObject 垃圾回收对象接口

2.6 架构设计优势

  1. 高复用性:AnimationEditor 本身不实现面板,而是通过 Persona 模块的工厂方法创建,最大化代码复用
  2. 可扩展性强 :通过 FExtensibilityManagerFAnimationEditorToolbarExtender 委托,外部模块可以自由扩展菜单和工具栏
  3. 职责清晰:模块接口、编辑器接口、编辑器实现、应用模式、命令定义各司其职,符合单一职责原则
  4. 布局可配置 :通过 FAnimationEditorMode::TabLayout 定义面板布局,修改布局不需要改动核心逻辑
  5. 与引擎深度集成:支持重导入回调、导入回调、垃圾回收、编辑器 Tick 等引擎基础设施

潜在改进点

  1. 曲线编辑状态管理 :当前 EditCurvesStopEditingCurves 之间没有显式的状态机,如果外部调用顺序不当可能导致状态不一致
  2. 错误处理 :部分操作(如 OnApplyCompressionOnRemoveBoneTrack)缺少失败时的回滚机制
  3. 文档注释:部分公开接口的方法缺少详细的参数说明和使用示例
  4. 测试覆盖:模块中没有包含自动化测试代码

3 类级代码注释详解

3.1 IAnimationEditorModule 接口

概述

IAnimationEditorModule 是 AnimationEditor 模块的公共接口,继承自 IModuleInterfaceIHasMenuExtensibilityIHasToolBarExtensibility。它为外部模块提供了创建动画编辑器实例、扩展菜单和工具栏的能力。

核心设计理念:

  • 工厂模式 :通过 CreateAnimationEditor() 创建编辑器实例,外部无需关心具体实现
  • 扩展性 :通过 FExtensibilityManagerFAnimationEditorToolbarExtender 委托提供两层扩展机制
  • 模块化 :只暴露必要的接口,具体实现在 FAnimationEditorModule

CreateAnimationEditor()

cpp 复制代码
virtual TSharedRef<IAnimationEditor> CreateAnimationEditor(
    const EToolkitMode::Type Mode,
    const TSharedPtr<IToolkitHost>& InitToolkitHost,
    class UAnimationAsset* InAnimationAsset) = 0;

功能分析:

  • 工厂方法,创建一个新的动画编辑器实例
  • Mode:工具集模式(独立窗口 / 嵌入到其他编辑器)
  • InitToolkitHost:工具集宿主,如果嵌入到其他编辑器则传入宿主引用
  • InAnimationAsset:要编辑的动画资产
  • 返回 TSharedRef<IAnimationEditor>,确保编辑器实例在调用者持有的引用释放前不会被销毁

实际实现(FAnimationEditorModule):

cpp 复制代码
virtual TSharedRef<IAnimationEditor> CreateAnimationEditor(...) override
{
    TSharedRef<FAnimationEditor> AnimationEditor(new FAnimationEditor());
    AnimationEditor->InitAnimationEditor(Mode, InitToolkitHost, InAnimationAsset);
    return AnimationEditor;
}

GetAllAnimationEditorToolbarExtenders()

cpp 复制代码
DECLARE_DELEGATE_RetVal_TwoParams(
    TSharedRef<FExtender>,
    FAnimationEditorToolbarExtender,
    const TSharedRef<FUICommandList> /*InCommandList*/,
    TSharedRef<IAnimationEditor> /*InAnimationEditor*/
);
virtual TArray<FAnimationEditorToolbarExtender>& GetAllAnimationEditorToolbarExtenders() = 0;

功能分析:

  • 这是 AnimationEditor 独有的扩展机制,区别于通用的 IHasToolBarExtensibility
  • FAnimationEditorToolbarExtender 委托接收命令列表和编辑器实例,返回一个 FExtender
  • 外部模块可以注册委托,在编辑器工具栏创建时动态添加扩展
  • 这个设计比通用的 FExtensibilityManager 更灵活,因为扩展器可以访问编辑器实例的完整状态

使用场景:

外部模块(如游戏项目模块)可以在 StartupModule() 中注册扩展器:

cpp 复制代码
IAnimationEditorModule& AnimEditorModule = FModuleManager::GetModuleChecked<IAnimationEditorModule>("AnimationEditor");
AnimEditorModule.GetAllAnimationEditorToolbarExtenders().Add(
    IAnimationEditorModule::FAnimationEditorToolbarExtender::CreateRaw(this, &FMyModule::ExtendAnimationEditorToolbar)
);

3.2 IAnimationEditor 接口

概述

IAnimationEditor 是动画编辑器的核心接口,继承自 FPersonaAssetEditorToolkitIHasPersonaToolkit。它定义了动画编辑器的公共 API,包括资产设置、资产浏览器访问和曲线编辑能力。


FCurveEditInfo 结构体

cpp 复制代码
struct FCurveEditInfo
{
    FText CurveDisplayName;      // 曲线的显示名称
    FLinearColor CurveColor;     // 曲线颜色
    FName CurveName;             // 曲线名称
    ERawCurveTrackTypes Type;    // 曲线类型
    int32 CurveIndex;            // 曲线索引
    FSimpleDelegate OnCurveModified; // 曲线修改回调
};

功能分析:

  • 用于描述一条需要编辑的曲线的完整信息
  • 支持 operator== 比较,通过 CurveName + Type + CurveIndex 三元组判断相等
  • 注意:5.3 版本中 FSmartName Name 已被标记为废弃,改用 FName CurveName

EditCurves()

cpp 复制代码
virtual void EditCurves(
    UAnimSequenceBase* InAnimSequence,
    const TArray<FCurveEditInfo>& InCurveInfo,
    const TSharedPtr<ITimeSliderController>& InExternalTimeSliderController) = 0;

功能分析:

  • 在动画编辑器中打开曲线编辑视图
  • InAnimSequence:要编辑曲线的动画序列
  • InCurveInfo:要编辑的曲线列表
  • InExternalTimeSliderController:外部时间滑块控制器,用于同步时间轴

实现逻辑(FAnimationEditor):

  1. 如果曲线编辑标签页不存在,通过 FPersonaModule 创建新的曲线编辑器 Widget
  2. 将曲线标签页插入到 TabManager 中
  3. 遍历 InCurveInfo,逐条添加曲线到曲线编辑器
  4. 调用 ZoomToFit() 自适应曲线范围

3.3 FAnimationEditor 类

概述

FAnimationEditor 是动画编辑器的核心实现类,继承自 IAnimationEditorFGCObjectFTickableEditorObject。它负责整个编辑器的生命周期管理、用户交互处理和面板协调。

多继承的设计意图:

  • IAnimationEditor:提供编辑器接口
  • FGCObject:需要手动管理 AnimationAsset 的 GC 引用(AddReferencedObjects
  • FTickableEditorObject:需要每帧 Tick 来刷新预览视口

3.3.1 初始化与生命周期

InitAnimationEditor()

cpp 复制代码
void FAnimationEditor::InitAnimationEditor(
    const EToolkitMode::Type Mode,
    const TSharedPtr<IToolkitHost>& InitToolkitHost,
    UAnimationAsset* InAnimationAsset)

功能分析(编辑器初始化全流程):

  1. 保存资产引用AnimationAsset = InAnimationAsset
  2. 注册导入/重导入回调 :监听 FReimportManager::OnPostReimport()UImportSubsystem::OnAssetPostImport,当资产被重新导入时自动刷新编辑器
  3. 创建 PersonaToolkit :通过 FPersonaModule::CreatePersonaToolkit() 创建 Persona 工具集,这是整个编辑器的核心基础设施
  4. 设置预览模式SetDefaultAnimationMode(EPreviewSceneDefaultAnimationMode::Animation) 将预览场景默认模式设为动画模式
  5. 创建 SkeletonTree :通过 ISkeletonEditorModule::CreateSkeletonTree() 创建骨骼树
  6. 初始化编辑器框架 :调用 FAssetEditorToolkit::InitAssetEditor() 初始化工具集基础框架
  7. 绑定命令BindCommands() 注册 UI 命令
  8. 创建应用模式AddApplicationMode() 添加 FAnimationEditorMode,并设为当前模式
  9. 扩展菜单和工具栏ExtendMenu() + ExtendToolbar()
  10. 打开文档标签页OpenNewAnimationDocumentTab(AnimationAsset) 打开初始动画文档

析构函数

cpp 复制代码
FAnimationEditor::~FAnimationEditor()
{
    GEditor->GetEditorSubsystem<UImportSubsystem>()->OnAssetPostImport.RemoveAll(this);
    FReimportManager::Instance()->OnPostReimport().RemoveAll(this);
    if (PersonaToolkit.IsValid())
    {
        constexpr bool bSetPreviewMeshInAsset = false;
        PersonaToolkit->SetPreviewMesh(nullptr, bSetPreviewMeshInAsset);
    }
}

功能分析:

  • 清理导入/重导入回调,防止悬空指针
  • 将预览网格设为 nullptr,确保 PersonaToolkit 释放对预览网格的引用
  • 注意:bSetPreviewMeshInAsset = false 表示不将预览网格写入资产,只是清理运行时的引用

3.3.2 Tick 机制

cpp 复制代码
void FAnimationEditor::Tick(float DeltaTime)
{
    if (GetPersonaToolkit()->GetMesh() && GetPersonaToolkit()->GetMesh()->IsCompiling())
    {
        return;  // 如果骨骼网格正在编译,跳过本帧 Tick
    }
    GetPersonaToolkit()->GetPreviewScene()->InvalidateViews();
}

功能分析:

  • 每帧调用 InvalidateViews() 刷新预览视口,确保动画预览实时更新
  • 聪明的优化:当骨骼网格正在编译时(IsCompiling()),跳过视口刷新,避免编译期间的不必要计算
  • 通过 FTickableEditorObjectGetTickableTickType() 返回 ETickableTickType::Always,确保即使在编辑器未激活时也保持 Tick

3.3.3 命令绑定

BindCommands()

cpp 复制代码
void FAnimationEditor::BindCommands()
{
    FAnimationEditorCommands::Register();
    
    ToolkitCommands->MapAction(FAnimationEditorCommands::Get().ApplyCompression,
        FExecuteAction::CreateSP(this, &FAnimationEditor::OnApplyCompression),
        FCanExecuteAction::CreateSP(this, &FAnimationEditor::HasValidAnimationSequence));
    
    ToolkitCommands->MapAction(FAnimationEditorCommands::Get().SetKey,
        FExecuteAction::CreateSP(this, &FAnimationEditor::OnSetKey),
        FCanExecuteAction::CreateSP(this, &FAnimationEditor::CanSetKey));
    // ... 更多命令绑定
}

功能分析:

  • 每个命令都绑定了一个 FExecuteAction(执行逻辑)和一个 FCanExecuteAction(可用性检查)
  • 这种设计模式确保了按钮的启用/禁用状态始终与当前编辑的资产类型保持一致
  • 例如,ApplyCompressionReimportAnimation 只在编辑 UAnimSequence 时可用(通过 HasValidAnimationSequence() 检查)
  • SetKey 还需要额外检查预览网格组件是否有选中的骨骼(BonesOfInterest.Num() > 0

3.3.4 动画操作

OnApplyCompression()

cpp 复制代码
void FAnimationEditor::OnApplyCompression()
{
    UAnimSequence* AnimSequence = Cast<UAnimSequence>(AnimationAsset);
    if (AnimSequence)
    {
        TArray<TWeakObjectPtr<UAnimSequence>> AnimSequences;
        AnimSequences.Add(AnimSequence);
        FPersonaModule& PersonaModule = FModuleManager::GetModuleChecked<FPersonaModule>("Persona");
        PersonaModule.ApplyCompression(AnimSequences, false);
    }
}

功能分析:

  • 将压缩逻辑委托给 FPersonaModule::ApplyCompression()
  • 使用 TWeakObjectPtr 传递动画序列,防止在压缩过程中资产被意外销毁
  • 第二个参数 false 表示不显示压缩对话框(直接使用默认设置)

OnReimportAnimation()

cpp 复制代码
void FAnimationEditor::OnReimportAnimation(bool bWithDialog)
{
    UAnimSequence* AnimSequence = Cast<UAnimSequence>(AnimationAsset);
    if (AnimSequence)
    {
        FReimportManager::Instance()->ReimportAsync(AnimSequence, ...);
    }
}

功能分析:

  • 支持两种重导入模式:bWithDialog=false(静默重导入)和 bWithDialog=true(显示重导入对话框)
  • 使用 ReimportAsync 异步重导入,不会阻塞编辑器 UI
  • 重导入完成后,通过 HandlePostReimport 回调自动刷新编辑器

OnRemoveBoneTrack()

cpp 复制代码
void FAnimationEditor::OnRemoveBoneTrack()
{
    if (FMessageDialog::Open(EAppMsgType::YesNo, LOCTEXT(...)) == EAppReturnType::Yes)
    {
        UAnimSequence* AnimSequence = Cast<UAnimSequence>(AnimationAsset);
        if (AnimSequence)
        {
            IAnimationDataController& Controller = AnimSequence->GetController();
            IAnimationDataController::FScopedBracket ScopedBracket(Controller, ...);
            Controller.RemoveAllBoneTracks();
            Controller.RemoveAllCurvesOfType(ERawCurveTrackTypes::RCT_Transform);
        }
    }
}

功能分析:

  • 这是一个破坏性操作,因此在执行前弹出确认对话框
  • 使用 IAnimationDataController::FScopedBracket 将多个操作包装为一个撤销单元
  • 先移除所有骨骼轨道,再移除所有 Transform 类型的曲线
  • 注意:不会移除通知(Notifies)和普通曲线

CopyCurveToSoundWave()

cpp 复制代码
void FAnimationEditor::CopyCurveToSoundWave(const FAssetData& SoundWaveAssetData) const
{
    USoundWave* SoundWave = Cast<USoundWave>(SoundWaveAssetData.GetAsset());
    UAnimSequence* Sequence = Cast<UAnimSequence>(AnimationAsset);
    // 遍历动画曲线,复制到 SoundWave 的内部曲线表
    for (const FFloatCurve& FloatCurve : Sequence->GetDataModel()->GetFloatCurves())
    {
        FRichCurve* Curve = FindOrAddCurve(CurveTable, FloatCurve.GetName());
        *Curve = FloatCurve.FloatCurve;
    }
    // 添加 Audio 曲线标记播放起始时间
    FRichCurve* AudioCurve = FindOrAddCurve(CurveTable, "Audio");
    AudioCurve->AddKey(0.f, 1.0f);
}

功能分析:

  • 这是一个高级功能,将动画的浮点曲线数据复制到 SoundWave 的内部曲线表
  • 常用于音频驱动的动画系统------SoundWave 中的曲线可以驱动游戏中的动画参数
  • 如果 SoundWave 没有内部曲线表,会自动创建一个
  • 会自动添加一个 Audio 曲线,标记音频开始播放的时间点

3.3.5 编辑器刷新

ConditionalRefreshEditor()

cpp 复制代码
void FAnimationEditor::ConditionalRefreshEditor(UObject* InObject)
{
    bool bInterestingAsset = true;
    // 检查是否与当前编辑的资产相关
    if (InObject != GetPersonaToolkit()->GetSkeleton() &&
        InObject != GetPersonaToolkit()->GetSkeleton()->GetPreviewMesh() &&
        Cast<UAnimationAsset>(InObject) != AnimationAsset)
    {
        bInterestingAsset = false;
    }
    // 检查是否是被编辑的 Montage 的引用动画
    if (UAnimMontage* Montage = Cast<UAnimMontage>(AnimationAsset))
    {
        for (FSlotAnimationTrack& Slot : Montage->SlotAnimTracks)
        {
            for (FAnimSegment& Segment : Slot.AnimTrack.AnimSegments)
            {
                if (Segment.GetAnimReference() == InObject)
                {
                    bInterestingAsset = true;
                    break;
                }
            }
        }
    }
    if (bInterestingAsset)
    {
        GetPersonaToolkit()->GetPreviewScene()->InvalidateViews();
        OpenNewAnimationDocumentTab(Cast<UAnimationAsset>(InObject));
    }
}

功能分析:

  • 当资产被重新导入时,判断是否需要刷新编辑器
  • "感兴趣"的资产包括:当前编辑的资产、关联的骨骼、预览网格、以及被编辑 Montage 引用的动画序列
  • 如果判断为相关资产,刷新视口并重新打开文档标签页

3.4 FAnimationEditorMode 类

概述

FAnimationEditorMode 继承自 FApplicationMode,是 AnimationEditor 唯一的应用模式。它定义了编辑器的面板布局和所有可用的面板工厂。

TabLayout 布局详解:

复制代码
┌──────────────────────────────────────────────────────────────┐
│  Standalone_AnimationEditor_Layout_v1.5                      │
├──────────────┬───────────────────────┬───────────────────────┤
│  左侧 (20%)  │   中间 (60%)          │   右侧 (20%)          │
├──────────────┼───────────────────────┼───────────────────────┤
│ SkeletonTree │   Viewport (60%)      │   Details             │
│ (骨骼树)     │   (视口)              │   (细节面板)          │
│              │                       │                       │
│ AssetDetails │                       │   AdvancedPreview     │
│ (资产详情)   │                       │   (高级预览设置)      │
│              ├───────────────────────┼───────────────────────┤
│              │   Document / Curve /  │   AssetBrowser        │
│              │   FindReplace (40%)   │   (资产浏览器)        │
│              │   (文档/曲线/查找替换)│                       │
│              │                       │   AnimMontageSections │
│              │                       │   (蒙太奇分段)        │
│              │                       │                       │
│              │                       │   CurveNames          │
│              │                       │   (曲线名称)          │
│              │                       │                       │
│              │                       │   SlotNames           │
│              │                       │   (插槽名称)          │
└──────────────┴───────────────────────┴───────────────────────┘

注册的面板工厂:

FAnimationEditorMode 构造函数中注册了以下面板:

面板 来源模块 用途
SkeletonTreeTab SkeletonEditor 骨骼层级结构树
DetailsTab Persona 选中对象的属性详情
ViewportTab Persona 3D 预览视口
AdvancedPreviewTab Persona 高级预览场景设置
AssetBrowserTab Persona 动画序列浏览器
AssetDetailsTab Persona 资产属性详情
CurveViewerTab Persona 动画曲线查看器
SlotNamesTab Persona 骨骼插槽名称管理
AnimNotifiesTab Persona 动画通知编辑
AnimMontageSectionsTab Persona 蒙太奇分段编辑
AnimAssetFindReplaceTab Persona 动画资产查找替换

可以看到,AnimationEditor 自身没有实现任何面板,全部通过 Persona 和 SkeletonEditor 模块的工厂方法创建。


3.5 FAnimationEditorCommands 类

概述

FAnimationEditorCommands 继承自 TCommands<FAnimationEditorCommands>,定义了 AnimationEditor 中所有可用的 UI 命令。

命令清单:

命令 描述 快捷键 可用条件
ImportAnimation 导入新动画 -
ReimportAnimation 重新导入当前动画 当前资产为 AnimSequence
ReimportAnimationWithDialog 带对话框重新导入 当前资产为 AnimSequence
ApplyCompression 应用压缩 当前资产为 AnimSequence
ExportToFBX_AnimData 导出动画数据到 FBX 当前资产为 AnimSequence
ExportToFBX_PreviewMesh 通过预览网格导出 FBX 当前资产为 AnimSequence
AddLoopingInterpolation 添加循环插值 当前资产为 AnimSequence
SetKey 设置关键帧 S 当前资产为 AnimSequence 且有选中骨骼
RemoveBoneTracks 移除所有骨骼轨道 当前资产为 AnimSequence

每个命令都通过 UI_COMMAND 宏定义,包含名称、描述和默认快捷键。CanExecute 条件在 FAnimationEditor::BindCommands() 中绑定。


3.6 UAnimationToolMenuContext 类

概述

UAnimationToolMenuContext 是一个轻量级的 UObject,用于在 UE5 的 UToolMenus 菜单系统中传递编辑器实例的引用。

cpp 复制代码
UCLASS()
class ANIMATIONEDITOR_API UAnimationToolMenuContext : public UObject
{
    GENERATED_BODY()
public:
    TWeakPtr<IAnimationEditor> AnimationEditor;
};

功能分析:

  • 持有 TWeakPtr<IAnimationEditor>,避免循环引用
  • FAnimationEditor::InitToolMenuContext() 中被创建并添加到 FToolMenuContext
  • FAnimationEditor::GetAnimationEditor() 静态方法中从 FToolMenuContext 中取出,用于在菜单回调中获取编辑器实例

这种设计模式在 UE5 编辑器模块中非常常见------由于 UToolMenus 的菜单回调是静态的或基于 UObject 的,需要一个上下文对象来传递编辑器实例。


3.7 FAnimationEditorModule 类

概述

FAnimationEditorModuleIAnimationEditorModule 的私有实现,负责模块的启动、关闭和编辑器实例的创建。

cpp 复制代码
class FAnimationEditorModule : public IAnimationEditorModule
{
    virtual void StartupModule() override
    {
        MenuExtensibilityManager = MakeShareable(new FExtensibilityManager);
        ToolBarExtensibilityManager = MakeShareable(new FExtensibilityManager);
    }

    virtual void ShutdownModule() override
    {
        MenuExtensibilityManager.Reset();
        ToolBarExtensibilityManager.Reset();
    }
    // ...
};

功能分析:

  • StartupModule():创建菜单和工具栏的扩展性管理器
  • ShutdownModule():清理扩展性管理器,释放资源
  • CreateAnimationEditor():创建 FAnimationEditor 实例并调用 InitAnimationEditor()
  • 最后通过 IMPLEMENT_MODULE(FAnimationEditorModule, AnimationEditor) 宏注册模块

4 功能使用示例编写

示例1:自定义动画编辑器工具栏扩展

假设我们的项目需要在动画编辑器的工具栏中添加一个"导出动画统计信息"的按钮。

cpp 复制代码
// MyAnimationEditorExtensions.h
#pragma once

#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"

class FMyAnimationEditorExtensions : public IModuleInterface
{
public:
    virtual void StartupModule() override;
    virtual void ShutdownModule() override;

private:
    // 工具栏扩展器
    TSharedRef<FExtender> ExtendAnimationEditorToolbar(
        const TSharedRef<FUICommandList> InCommandList,
        TSharedRef<IAnimationEditor> InAnimationEditor);
    
    // 导出统计信息的处理函数
    void OnExportAnimationStats(TSharedRef<IAnimationEditor> AnimationEditor);
    
    // 自定义命令列表
    TSharedPtr<FUICommandList> CommandList;
};

// MyAnimationEditorCommands.h
#pragma once

#include "Framework/Commands/Commands.h"

class FMyAnimationEditorCommands : public TCommands<FMyAnimationEditorCommands>
{
public:
    FMyAnimationEditorCommands()
        : TCommands<FMyAnimationEditorCommands>(
            TEXT("MyAnimationEditor"),
            NSLOCTEXT("Contexts", "MyAnimationEditor", "My Animation Editor"),
            NAME_None,
            FAppStyle::GetAppStyleSetName())
    {}

    virtual void RegisterCommands() override;

    TSharedPtr<FUICommandInfo> ExportAnimationStats;
};
cpp 复制代码
// MyAnimationEditorExtensions.cpp
#include "MyAnimationEditorExtensions.h"
#include "IAnimationEditorModule.h"
#include "Animation/AnimSequence.h"
#include "ToolMenus.h"

void FMyAnimationEditorCommands::RegisterCommands()
{
    UI_COMMAND(ExportAnimationStats, "导出动画统计", "导出当前动画的统计信息到CSV", 
        EUserInterfaceActionType::Button, FInputChord());
}

void FMyAnimationEditorExtensions::StartupModule()
{
    FMyAnimationEditorCommands::Register();
    
    CommandList = MakeShareable(new FUICommandList);
    
    // 注册工具栏扩展器
    IAnimationEditorModule& AnimEditorModule = 
        FModuleManager::GetModuleChecked<IAnimationEditorModule>("AnimationEditor");
    AnimEditorModule.GetAllAnimationEditorToolbarExtenders().Add(
        IAnimationEditorModule::FAnimationEditorToolbarExtender::CreateRaw(
            this, &FMyAnimationEditorExtensions::ExtendAnimationEditorToolbar));
}

TSharedRef<FExtender> FMyAnimationEditorExtensions::ExtendAnimationEditorToolbar(
    const TSharedRef<FUICommandList> InCommandList,
    TSharedRef<IAnimationEditor> InAnimationEditor)
{
    TSharedRef<FExtender> Extender = MakeShareable(new FExtender);
    
    // 绑定命令
    CommandList->MapAction(
        FMyAnimationEditorCommands::Get().ExportAnimationStats,
        FExecuteAction::CreateStatic(&FMyAnimationEditorExtensions::OnExportAnimationStats, InAnimationEditor)
    );
    
    // 在 Animation 段之后添加按钮
    Extender->AddToolBarExtension(
        "Animation",
        EExtensionHook::After,
        CommandList,
        FToolBarExtensionDelegate::CreateLambda([this](FToolBarBuilder& ToolbarBuilder)
        {
            ToolbarBuilder.AddToolBarButton(FMyAnimationEditorCommands::Get().ExportAnimationStats);
        })
    );
    
    return Extender;
}

void FMyAnimationEditorExtensions::OnExportAnimationStats(TSharedRef<IAnimationEditor> AnimationEditor)
{
    // 通过 PersonaToolkit 获取当前编辑的动画资产
    // 具体实现取决于项目需求
}

示例2:通过资产工具打开动画编辑器

cpp 复制代码
// 在自定义的 AssetTools 模块中
void FMyAssetTools::OpenAnimationEditor(UAnimationAsset* AnimationAsset)
{
    if (AnimationAsset)
    {
        IAnimationEditorModule& AnimEditorModule = 
            FModuleManager::GetModuleChecked<IAnimationEditorModule>("AnimationEditor");
        
        TSharedRef<IAnimationEditor> Editor = AnimEditorModule.CreateAnimationEditor(
            EToolkitMode::Standalone,
            nullptr,  // 独立窗口
            AnimationAsset
        );
    }
}

示例3:监听动画编辑器事件

cpp 复制代码
// 在项目模块中监听动画编辑器的创建
void FMyGameEditorModule::StartupModule()
{
    // 注册 Persona 模块的委托
    FPersonaModule& PersonaModule = 
        FModuleManager::GetModuleChecked<FPersonaModule>("Persona");
    
    PersonaModule.OnRegisterTabs().AddRaw(this, &FMyGameEditorModule::OnRegisterAnimationEditorTabs);
    PersonaModule.OnRegisterLayoutExtensions().AddRaw(
        this, &FMyGameEditorModule::OnRegisterAnimationEditorLayoutExtensions);
}

void FMyGameEditorModule::OnRegisterAnimationEditorTabs(
    FWorkflowAllowedTabSet& TabFactories,
    TSharedRef<FWorkflowCentricApplication> InApplication)
{
    // 检查是否是动画编辑器
    if (InApplication->GetToolkitFName() == FName("AnimationEditor"))
    {
        // 注册自定义面板
        // TabFactories.RegisterFactory(...);
    }
}

5 如何扩展 AnimationEditor

5.1 扩展工具栏

AnimationEditor 提供了两种工具栏扩展方式:

方式一:通用扩展管理器(IHasToolBarExtensibility

cpp 复制代码
IAnimationEditorModule& Module = FModuleManager::GetModuleChecked<IAnimationEditorModule>("AnimationEditor");
TSharedPtr<FExtensibilityManager> ExtMgr = Module.GetToolBarExtensibilityManager();
ExtMgr->AddExtender(MyExtender);

方式二:专用工具栏扩展器委托(推荐)

cpp 复制代码
Module.GetAllAnimationEditorToolbarExtenders().Add(
    IAnimationEditorModule::FAnimationEditorToolbarExtender::CreateLambda(
        [](const TSharedRef<FUICommandList>& Commands, TSharedRef<IAnimationEditor> Editor) -> TSharedRef<FExtender>
        {
            TSharedRef<FExtender> Extender = MakeShareable(new FExtender);
            // 添加自定义扩展...
            return Extender;
        }
    )
);

方式二的优点是可以直接访问编辑器实例,获取当前编辑的资产、骨骼等信息。

5.2 扩展菜单

通过 GetMenuExtensibilityManager() 获取菜单扩展管理器:

cpp 复制代码
IAnimationEditorModule& Module = FModuleManager::GetModuleChecked<IAnimationEditorModule>("AnimationEditor");
Module.GetMenuExtensibilityManager()->AddExtender(MyMenuExtender);

5.3 注册自定义面板

通过 PersonaModule.OnRegisterTabs() 委托,在动画编辑器创建时注册自定义面板:

cpp 复制代码
FPersonaModule& PersonaModule = FModuleManager::GetModuleChecked<FPersonaModule>("Persona");
PersonaModule.OnRegisterTabs().AddLambda(
    [](FWorkflowAllowedTabSet& TabFactories, TSharedRef<FWorkflowCentricApplication> InApplication)
    {
        if (InApplication->GetToolkitFName() == FName("AnimationEditor"))
        {
            // 注册自定义面板工厂
            TabFactories.RegisterFactory(MyCustomTabFactory);
        }
    }
);

5.4 扩展布局

通过 PersonaModule.OnRegisterLayoutExtensions() 委托,在动画编辑器布局中插入自定义面板位置:

cpp 复制代码
PersonaModule.OnRegisterLayoutExtensions().AddLambda(
    [](FLayoutExtender& Extender)
    {
        // 在 AssetBrowser 堆栈中插入自定义面板
        Extender.ExtendLayout(
            AnimationEditorTabs::AssetBrowserTab,
            ELayoutExtensionPosition::After,
            FTabManager::FTab(MyCustomTabName, ETabState::ClosedTab)
        );
    }
);

6 总结与最佳实践

核心要点

  1. AnimationEditor 是组装者,不是实现者

    • 它不实现任何面板,而是通过 Persona 模块的工厂方法创建面板
    • 它的核心职责是:定义布局、协调面板、处理动画操作
  2. 理解 Persona 框架是理解 AnimationEditor 的前提

    • IPersonaToolkit 是整个编辑器的核心基础设施
    • FPersonaModule 提供了几乎所有面板的工厂方法
    • FWorkflowCentricApplication 提供了应用模式和 Tab 管理
  3. 扩展机制是设计亮点

    • 两层工具栏扩展:通用 FExtensibilityManager + 专用 FAnimationEditorToolbarExtender
    • 运行时面板注册:OnRegisterTabs() + OnRegisterLayoutExtensions()
    • 外部模块可以完全自定义动画编辑器的工具栏、菜单和面板布局
  4. 命令绑定的可用性检查

    • 每个命令都绑定了 CanExecute 条件,根据当前编辑的资产类型动态启用/禁用
    • 这是一个好的实践,避免了用户在不合适的上下文中执行操作
  5. 生命周期管理

    • 通过 FGCObject 手动管理 AnimationAsset 的 GC 引用
    • 通过 FTickableEditorObject 每帧刷新预览视口
    • 在析构函数中清理所有回调,防止悬空指针

最佳实践

  1. 扩展 AnimationEditor 时优先使用专用扩展器委托

    • FAnimationEditorToolbarExtender 比通用 FExtensibilityManager 更灵活
    • 可以直接访问编辑器实例和命令列表
  2. 利用 Persona 模块的委托进行深度扩展

    • OnRegisterTabs() 注册自定义面板
    • OnRegisterLayoutExtensions() 调整布局
    • 不需要修改 AnimationEditor 的一行代码
  3. 在重导入回调中正确处理编辑器刷新

    • 参考 ConditionalRefreshEditor() 的逻辑
    • 判断资产是否与当前编辑器相关,避免不必要的刷新
  4. 使用 IAnimationDataController 进行动画数据修改

    • 不要直接修改动画数据,而是通过 Controller 接口
    • 使用 FScopedBracket 将多个操作包装为一个撤销单元
  5. 为自定义扩展正确管理命令的生命周期

    • 在模块 StartupModule() 中注册命令
    • 在模块 ShutdownModule() 中清理

常见问题解决

问题1:扩展的工具栏按钮不显示

  • 检查命令是否已注册(RegisterCommands()
  • 检查 CanExecute 条件是否返回 true
  • 确认扩展钩子位置(EExtensionHook::After 等)是否正确

问题2:自定义面板在动画编辑器中不出现

  • 确认 OnRegisterTabs() 回调中正确过滤了 ToolkitFName
  • 检查 TabLayout 中是否为自定义面板预留了位置(通过 OnRegisterLayoutExtensions() 扩展)

问题3:重导入后编辑器没有自动刷新

  • 确认 HandlePostReimport 回调已正确绑定
  • 检查 ConditionalRefreshEditor 中的资产关联判断逻辑

附录

A. 相关资源

  • Persona 模块源码:Engine/Source/Editor/Persona
  • SkeletonEditor 模块源码:Engine/Source/Editor/SkeletonEditor
  • AnimGraph 模块源码:Engine/Source/Editor/AnimGraph
  • UE5 官方文档:Animation Editor
  • UE5 官方文档:Workflow-Centric Applications

B. 文件结构参考

复制代码
AnimationEditor/
├── AnimationEditor.Build.cs
├── Public/
│   ├── IAnimationEditor.h              // 编辑器接口
│   ├── IAnimationEditorModule.h        // 模块接口
│   └── AnimationToolMenuContext.h      // 菜单上下文
└── Private/
    ├── AnimationEditor.h               // 编辑器实现(头文件)
    ├── AnimationEditor.cpp             // 编辑器实现(源文件)
    ├── AnimationEditorMode.h           // 应用模式
    ├── AnimationEditorMode.cpp
    ├── AnimationEditorCommands.h       // 命令定义
    ├── AnimationEditorCommands.cpp
    └── AnimationEditorModule.cpp       // 模块实现
相关推荐
mengzhi啊1 天前
UE5 把剑绑定到骨骼-给要播放的动画及动画蒙太奇添加武器
ue5
directx3d_beginner1 天前
32,每帧更新怪物朝向改为c++
ue5
吴梓穆2 天前
UE5 播放透明视频
ue5
日月云棠2 天前
UE5源码分析之Editor——AnimationBlueprintEditor模块全面分析
ue5
远离UE42 天前
UE5 不同贴图采样类型的区别
ue5
日月云棠3 天前
UE5源码分析——ActorPickerMode模块全面分析
ue5
weixin_404679314 天前
虚幻5 如何打开关卡蓝图
ue5
dong1326976 天前
UE5FPS游戏开发教程(一)
ue5
朗迹 - 张伟8 天前
UE5.8 用Trae的AI开发功能
ue5