From 52d7656c306b15205b03b45c78c9a69deb397432 Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Sat, 11 Jul 2026 16:17:09 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=8A=A8=E7=94=BB=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E6=9C=AA=E5=AE=8C=E6=88=90=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Docs/动画系统需求整理.md | 601 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 601 insertions(+) create mode 100644 Docs/动画系统需求整理.md diff --git a/Docs/动画系统需求整理.md b/Docs/动画系统需求整理.md new file mode 100644 index 000000000..6a97617b8 --- /dev/null +++ b/Docs/动画系统需求整理.md @@ -0,0 +1,601 @@ +# 自研帧动画系统需求文档 + +> 当前文档处于需求梳理阶段。目标是先把系统边界、数据结构、编辑器工作流和运行时接入方式说清楚,再进入具体技术设计与实现。 + +## 1. 背景与问题 + +### 1.1 项目背景 + +本项目是 2D 像素风格 AVG / 视觉小说游戏,动画主要服务于角色立绘、场景物件和少量 UI 表现。当前项目中,大部分序列帧动画显示在 `SpriteRenderer` 上,少量显示在 UI `Image` 上。 + +现阶段主要使用 Unity 原生 Animator / AnimationClip / Timeline 等系统完成表现。其中 Animator 更适合状态机式角色动作,而本项目大量需求更接近“演出编排”:播放一个片段、接另一个片段、最后进入 Idle,或者停留在某一帧。 + +### 1.2 当前痛点 + +#### Unity Animator / AnimationClip 相关 + +1. 状态机模型与项目需求不匹配 + Unity Animator 的核心是状态机,适合玩家可控角色在多个状态之间切换;本项目常见需求是线性演出、片段编排、播完后进入 Idle 或停在终帧。 + +2. 资产结构臃肿 + 简单序列帧动画也需要 `Animator Controller`、`Animation Clip`、Sprite 绑定等多层资源。资源关系依赖 Unity 序列化引用,更新和排查成本高。 + +3. 预览体验差 + Animator Controller 难以直接预览,Animation Clip 预览通常需要依附 GameObject。策划或美术无法轻松检查一个片段或一组片段的最终效果。 + +4. 帧内容不可读、不可维护 + Animation Clip 内部通过 Unity 对象引用记录 Sprite,难以直观看到每一帧具体使用了哪个 Sprite、持续多久,也不方便做批量替换或差异检查。 + +#### 美术资产导入相关 + +1. Aseprite 导入链路不稳定 + 当前美术使用 Aseprite 生产资源。Unity Aseprite Importer 存在中文 Tag 支持不佳、资源更新后 Clip 重新生成导致引用关系变化等问题。 + +2. 现有 AnimationClip 生成器只能部分解决问题 + 项目中已有 PNG + JSON -> 切图 -> 生成 AnimationClip 的工具,但最终产物仍然是 Unity AnimationClip,仍然保留了 Animator / Clip 体系的维护成本。 + +3. 美术更新成本高 + 理想流程应当是:美术更新 PNG / JSON 后,策划或开发点击刷新,已有动画资产保持引用不变,只更新帧数据和 Sprite 映射。 + +### 1.3 文档目标 + +本文档希望定义一个独立于 Unity Animator Controller 的自研帧动画系统,重点解决: + +- 序列帧动画的数据结构 +- PNG / Aseprite JSON 等来源的导入与刷新 +- Clip / AnimationSet / Sequence 的组织关系 +- 编辑器内预览、检查和编排工作流 +- 运行时播放器接口 +- 与现有 Actor、Yarn、AnimatorCenter、存档系统的兼容与迁移路径 + +## 2. 系统目标与非目标 + +### 2.1 核心目标 + +1. 支持 SpriteRenderer 和 UI Image 的序列帧播放。 +2. 支持单个动画片段独立播放、预览、刷新和检查。 +3. 支持同一对象的一组动画片段集中管理。 +4. 支持简单演出编排,例如“播放一次动作 -> 进入 Idle 循环 -> 停在最后一帧”。 +5. 支持从 Aseprite JSON / PNG 生成动画数据,并在资源更新时保持动画资产引用稳定。 +6. 提供不依赖场景 GameObject 的编辑器预览能力。 +7. 为运行时代码提供清晰、稳定、可迁移的播放 API。 + +### 2.2 非目标 + +第一阶段不尝试替代以下系统: + +1. Unity Timeline 的多轨演出能力。 +2. DOTween 等 Tween 动效系统。 +3. Cinemachine 镜头系统。 +4. 骨骼动画、网格变形、复杂 Transform 曲线动画。 +5. 完整可视化节点编辑器或通用状态机编辑器。 + +### 2.3 第一阶段范围建议 + +第一阶段建议聚焦: + +- `FrameClip`:单个序列帧片段。 +- `AnimationSet`:同一对象的一组 `FrameClip`。 +- 简单 `Sequence`:线性片段队列 + 结束行为。 +- `FrameAnimationPlayer`:运行时播放器。 +- Clip / Set 的基础编辑器和预览。 + +复杂图编辑器、条件分支、随机播放、批量迁移工具可以放到后续阶段。 + +## 3. 术语与概念 + +### 3.1 Frame + +单帧数据,描述某一时间段内应该显示的 Sprite。 + +候选字段: + +- `Sprite sprite` +- `float duration` +- `string frameName` +- `int sourceIndex` + +待讨论: + +- `duration` 使用秒还是毫秒? +- 是否需要记录来源 rect / pivot / tag 等导入信息? + +### 3.2 FrameClip + +最小可播放动画片段,由一组 Frame 组成。 + +候选职责: + +- 保存帧列表。 +- 保存默认播放速度。 +- 保存默认结束行为。 +- 保存导入来源。 +- 提供总时长、帧数等只读信息。 + +待讨论: + +- Clip 是否应当保存 loop,还是只保存 default end behavior? +- 导入生成的 Clip 是否允许手动编辑帧表? +- Clip 作为独立 `.asset` 保存,还是作为 AnimationSet 的 sub-asset 保存? + +### 3.3 AnimationSet + +同一对象的一组动画片段集合。例如一个角色、一个场景物件或一个 UI 元件的所有动画。 + +候选职责: + +- 维护 Clip 名称到 Clip 的映射。 +- 维护默认 Idle Clip。 +- 维护导入来源列表。 +- 支持刷新导入来源后按名称更新已有 Clip。 +- 提供编辑器集中预览。 + +待讨论: + +- 原文中的 `Library` 是否改名为 `AnimationSet`? +- Set 内 Clip 是否必须唯一命名? +- Set 是否负责 Sequence,还是 Sequence 独立成资产? + +### 3.4 Sequence + +一段演出编排,描述多个 Clip 的播放顺序和结束策略。 + +候选能力: + +- 线性播放:`Clip A -> Clip B -> Clip C` +- 播完进入 Idle:`Intro -> Idle(loop)` +- 播完停在最后一帧 +- 播完隐藏目标 + +待讨论: + +- 第一阶段是否只做线性 Sequence? +- Sequence 是否需要独立资产? +- Sequence 是否应该支持分支、随机、条件判断? + +### 3.5 Player / Controller + +运行时负责推进时间、设置 Sprite、响应代码调用的组件。 + +候选拆分: + +- `FrameAnimationPlayer`:底层播放逻辑。 +- `FrameAnimationController`:挂载在 GameObject 上,对外暴露 `Play("clipName")` 等接口。 +- `IFrameAnimationTarget`:统一封装 SpriteRenderer / Image。 + +待讨论: + +- 是否需要一个类似 `AnimatorCenter` 的全局注册中心? +- 是否沿用现有 `animatorName + stateName` 的外部调用模型? + +## 4. 数据结构需求 + +### 4.1 FrameClip 数据 + +候选字段: + +```csharp +string clipName; +List frames; +float speed; +EndBehavior defaultEndBehavior; +ImportSource importSource; +bool isGenerated; +``` + +候选结束行为: + +- `Stop` +- `HoldLastFrame` +- `Loop` +- `Clear` +- `HideTarget` + +待讨论: + +- `Stop` 和 `HoldLastFrame` 是否需要区分? +- 循环播放是 Clip 的默认属性,还是 Play 请求的属性? + +### 4.2 AnimationSet 数据 + +候选字段: + +```csharp +string setName; +List clips; +string defaultClipName; +List importSources; +``` + +待讨论: + +- Clip 引用外部资产,还是作为 Set 的子资产? +- 如果刷新后某个 Clip 名称消失,应该删除、标记 missing,还是保留旧数据? +- 如果刷新后出现同名 Clip,如何处理冲突? + +### 4.3 Sequence 数据 + +候选字段: + +```csharp +string sequenceName; +List steps; +EndBehavior finalBehavior; +string fallbackIdleClipName; +``` + +待讨论: + +- `SequenceStep` 是否只需要 clipName + overrideEndBehavior? +- 是否需要 step-level speed、事件、等待时间? +- 是否需要在某一帧触发事件? + +### 4.4 ImportSource 数据 + +候选来源类型: + +- `Manual` +- `AsepriteJsonAndTexture` +- `ManualGridJsonAndTexture` + +候选字段: + +```csharp +ImportSourceType type; +Texture2D texture; +TextAsset json; +Vector2 pivot; +bool generateSpritesIfNeeded; +``` + +待讨论: + +- 是否允许一个 AnimationSet 管理多组 PNG / JSON? +- 切图结果是否直接修改原 Texture Importer? +- 是否需要保存上次导入摘要,用于显示差异? + +## 5. 资产导入与刷新需求 + +### 5.1 Aseprite JSON 导入 + +系统应支持读取 Aseprite 导出的 JSON: + +- 读取 `frames` 中的帧 rect 和 duration。 +- 读取 `meta.frameTags` 作为 Clip 名称和帧范围。 +- 支持 `forward`、`reverse`、`pingpong` 等方向。 +- 支持中文 Tag 名称。 + +待讨论: + +- 是否在导入时保留 Aseprite 原始 frameName? +- `pingpong` 是否展开成实际帧列表? + +### 5.2 手动 Grid JSON 导入 + +系统可以复用现有生成器里的手动 JSON 思路: + +- 配置 rows / columns / frameCount。 +- 配置全局 frameDuration。 +- 配置每个动画片段的 frameIndices。 + +待讨论: + +- 这个格式是否继续保留? +- 是否需要提供 JSON 模板和校验工具? + +### 5.3 刷新策略 + +刷新时应尽量保持已有资产引用稳定。 + +候选规则: + +1. 以 Clip 名称作为匹配键。 +2. 同名 Clip 原地更新帧数据。 +3. 新 Clip 自动加入。 +4. 消失的 Clip 标记为 missing,等待用户确认是否删除。 +5. 手动修改过的字段不被刷新覆盖,除非用户选择强制刷新。 + +待讨论: + +- 哪些字段属于导入生成,哪些字段允许用户覆盖? +- 是否需要刷新前预览差异? + +## 6. 编辑器需求 + +### 6.1 FrameClip Inspector + +基础能力: + +- 显示 Clip 名称、总时长、帧数、默认结束行为。 +- 显示帧表:序号、Sprite、duration、来源 frameName。 +- 支持不依赖场景 GameObject 的预览。 +- 支持播放、暂停、逐帧、调整预览速度。 + +手动 Clip: + +- 帧表可编辑。 +- 可增删帧、替换 Sprite、修改 duration。 + +导入 Clip: + +- 帧表默认只读。 +- 可以跳转到 ImportSource。 +- 可以刷新来源。 + +待讨论: + +- 是否允许导入 Clip 局部覆盖某一帧? +- 预览区域是否需要显示透明棋盘格、原始尺寸、缩放倍率? + +### 6.2 AnimationSet Editor + +基础能力: + +- 显示 Set 内所有 Clip。 +- 每个 Clip 有小预览窗口。 +- 支持搜索、排序、重命名、检查重复名。 +- 支持设置默认 Idle / 默认 Clip。 +- 支持从导入来源批量刷新。 +- 支持侧边预览完整 Sequence 或单个 Clip。 + +待讨论: + +- 第一版是否做独立 EditorWindow,而不是只做 Inspector? +- 是否需要拖拽排序? +- Clip 小窗全部实时播放是否会影响编辑器性能? + +### 6.3 Sequence Editor + +第一阶段建议做轻量列表式编辑: + +- 添加 Step。 +- 选择 Clip。 +- 设置 Step 播放策略。 +- 设置最终行为。 +- 一键从头预览。 + +后续再考虑节点图或连线式编辑。 + +待讨论: + +- 是否真的需要节点图? +- Sequence 是否需要与 Yarn / Timeline 联动显示? + +### 6.4 校验与错误提示 + +编辑器应能检查: + +- 空 Sprite。 +- duration 小于等于 0。 +- Clip 重名。 +- Sequence 引用不存在的 Clip。 +- ImportSource 缺少 texture 或 json。 +- JSON 中 Tag 重名。 +- 刷新后丢失的 Clip。 + +## 7. 运行时需求 + +### 7.1 播放目标 + +必须支持: + +- `SpriteRenderer` +- `UnityEngine.UI.Image` + +可选支持: + +- 未来扩展到其他自定义 Sprite 显示组件。 + +### 7.2 播放接口 + +候选接口: + +```csharp +Play(string clipName); +Play(string clipName, EndBehavior endBehavior); +Loop(string clipName); +Queue(string clipName); +Stop(); +Pause(); +Resume(); +Seek(float timeSeconds); +SetSpeed(float speed); +GetCurrentClipName(); +GetPlaybackTime(); +GetDuration(); +``` + +待讨论: + +- `Loop` 是否只是 `Play(..., Loop)` 的快捷方法? +- `Queue` 第一阶段是否需要? +- 是否需要异步协程接口:`IEnumerator PlayAsync(...)`? + +### 7.3 播放行为 + +需要明确: + +- 播放新 Clip 时是否从第 0 帧开始。 +- 播放同一 Clip 时是否重播。 +- Stop 后停在哪一帧。 +- Pause 是否冻结当前帧。 +- HideTarget 是否由播放器设置 GameObject active,还是只清空 Sprite / alpha。 + +### 7.4 时间推进 + +待设计: + +- 使用 `Update()` 基于 `Time.deltaTime` 推进。 +- 是否支持 unscaled time。 +- 是否支持手动 Evaluate,供编辑器预览和存档恢复使用。 + +## 8. 与现有项目系统的关系 + +### 8.1 Actor 系统 + +当前 `ActorAnima` 通过 `Animation/{ActorName}` 加载 AnimatorController,并通过状态名播放。 + +新系统需要考虑: + +- 是否新增 `ActorFrameAnima` 类型。 +- 是否替换 `ActorAnima` 内部实现。 +- 存档中 `stateName` / `stateNormalizedTime` 如何兼容。 +- Yarn 中现有 set_actor_state 等命令是否需要调整。 + +待讨论: + +- 第一批迁移对象是否选择角色立绘? +- 还是先选择孤立的场景物件 / UI 动画做试点? + +### 8.2 AnimatorCenter / AnimatorHandler + +当前通用动画调用以 `animatorName + stateName` 为核心。 + +新系统可以选择: + +1. 新增并行的 `FrameAnimationCenter`。 +2. 让 `AnimatorCenter` 逐步兼容新播放器。 +3. 保持两套系统并存,由 Yarn 命令区分调用。 + +待讨论: + +- 为减少 Yarn 改动,是否应尽量保持类似 API? +- 旧 Animator 动画是否长期保留? + +### 8.3 Yarn 命令 + +候选新命令: + +```yarn +<> +<> +<> +``` + +待讨论: + +- 是否复用现有 `play_animation` 命令? +- 命令是否等待播放完成? +- Loop Clip 的等待语义如何定义? + +### 8.4 Timeline + +第一阶段不替代 Timeline。 + +新系统只需要考虑: + +- Timeline 是否可以调用 FrameAnimationController。 +- Frame 动画是否需要在 Timeline 中被录制或控制。 + +### 8.5 存档系统 + +需要定义快照语义: + +- 当前 Clip 名称。 +- 当前播放时间或帧索引。 +- 当前播放状态:playing / paused / stopped。 +- 当前结束行为。 +- 是否保存队列。 + +待讨论: + +- 对角色立绘保存精确播放时间。 +- 对一次性演出只保存终态,避免读档后重复播放。 + +## 9. 迁移计划 + +### 9.1 第一阶段:原型验证 + +目标: + +- 实现最小 FrameClip。 +- 实现 SpriteRenderer / Image 播放。 +- 实现基础 Inspector 预览。 +- 从一个简单 PNG / JSON 生成 Clip。 + +验收: + +- 不依赖 AnimatorController 播放序列帧。 +- 编辑器中可直接预览 Clip。 +- 修改来源后能刷新 Clip。 + +### 9.2 第二阶段:AnimationSet 与运行时接入 + +目标: + +- 实现 AnimationSet。 +- 实现按名称播放 Clip。 +- 实现 AnimationSet 编辑器。 +- 在一个非核心场景物件上试点。 + +验收: + +- 代码可通过名称播放 Set 内 Clip。 +- 编辑器可集中预览和检查所有 Clip。 + +### 9.3 第三阶段:Sequence 与演出工作流 + +目标: + +- 实现简单线性 Sequence。 +- 支持“播放一次 -> 进入 Idle loop”。 +- 提供 Sequence 预览。 + +验收: + +- 策划可以不写代码配置基础演出序列。 + +### 9.4 第四阶段:Actor / Yarn 迁移 + +目标: + +- 选择一个角色或一组立绘动画试点。 +- 接入 Yarn 命令。 +- 接入存档恢复。 + +验收: + +- 角色动画可以通过新系统播放、保存、恢复。 +- 不破坏旧 Animator 动画。 + +## 10. 验收标准 + +第一版完成时,应满足: + +1. 能创建和保存 FrameClip 资产。 +2. 能在 Inspector 或 EditorWindow 中直接预览 Clip。 +3. 能从 PNG / JSON 生成或刷新 Clip。 +4. 能挂载播放器到 SpriteRenderer / Image 并播放 Clip。 +5. 能通过 AnimationSet 按名称播放 Clip。 +6. 能检查基础错误并给出明确提示。 +7. 刷新导入资源时,不破坏已存在 Clip 的引用。 + +## 11. 待讨论问题清单 + +优先级较高: + +1. `Library` 是否正式命名为 `AnimationSet`? +2. Clip 的循环/结束策略放在哪里最合适? +3. Clip 独立资产与 Set 子资产,哪种更适合项目工作流? +4. 第一阶段试点对象选角色立绘、场景物件,还是 UI 动画? +5. 是否保留现有 `AnimatorCenter` API 形状,降低 Yarn 迁移成本? +6. 存档是否需要保存动画播放中间态? + +优先级较低: + +1. 是否需要节点图式 Sequence 编辑器? +2. 是否需要帧事件? +3. 是否支持随机播放或条件分支? +4. 是否需要 Timeline 轨道扩展? + +## 12. 当前倾向 + +当前建议: + +1. 第一版只做序列帧系统,不做通用动画系统。 +2. 命名采用 `FrameClip` / `AnimationSet` / `Sequence`。 +3. Clip 保存默认结束行为,但播放请求可以覆盖。 +4. 导入刷新以 Clip 名称为稳定匹配键。 +5. 第一版 Sequence 使用列表式编辑,不做节点图。 +6. 运行时底层直接按时间设置 Sprite,不使用 Unity Animator / Playables。 +7. 先新增并行系统,验证稳定后再讨论替换 ActorAnima / AnimatorCenter。