Files
aibis-dream/Docs/帧动画系统第一二阶段手动测试.md
T

442 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 帧动画系统第一、二阶段手动测试方案
## 1. 测试目标
本文档用于手动验收帧动画系统前两个阶段:
- 第一阶段:数据模型、Graph 解析、运行时播放器、统一求值器和播放 Handle。
- 第二阶段:Aseprite JSON 解析、ImportSource、差异预览、稳定刷新、只读 Sprite 匹配和自动切图。
测试重点是验证实际工作流和资产稳定性,不验收第三阶段的完整 Graph 工作台、节点画布或正式删除流程。
## 2. 测试前准备
### 2.1 环境
- Unity2022.3.7f1c1。
- 打开项目后等待脚本编译完成。
- 清空 Console,确认没有编译错误。
- 打开 `Window > General > Test Runner`,确认可以看到:
- `AibisDream.FrameAnimation.Tests.EditMode`
- `AibisDream.FrameAnimation.Tests.PlayMode`
### 2.2 生成测试样例
依次执行:
1. `Tools > Frame Animation > Rebuild Runtime Sample`
2. `Tools > Frame Animation > Rebuild Import Sample`
生成内容:
- 运行时样例场景:`Assets/Scenes/FrameAnimationRuntimeTest.unity`
- 第一阶段样例:`Assets/GameContent/Test/FrameAnimation/`
- 第二阶段样例:`Assets/GameContent/Test/FrameAnimation/Import/`
第二阶段测试中可以修改 Import 文件夹内的测试 JSON。测试结束后再次执行 `Rebuild Import Sample` 即可恢复,不要修改生产动画资源。
### 2.3 测试记录
每个用例记录:
| 项目 | 内容 |
|---|---|
| 结果 | 通过 / 失败 / 阻塞 |
| 实际表现 | 简述观察结果 |
| Console | 是否出现 Error/Exception |
| 证据 | 截图、录屏或相关资产路径 |
| 缺陷 | 可复现步骤和预期/实际差异 |
## 3. 第一阶段:运行时播放测试
### RT-01 样例场景基础播放(P0)
操作:
1. 打开 `FrameAnimationRuntimeTest.unity`
2. 进入 Play Mode。
3. 同时观察左侧 SpriteRenderer 和右侧 UI Image。
4. 选中两个对象,在 `FrameAnimationPlayer` Inspector 中观察 Runtime State。
预期:
- 左侧 `Flow Sample - SpriteRenderer` 立即显示 Intro 第 0 帧。
- 左侧约 1.25 秒后从 Intro 自动进入 Idle,之后持续循环 Idle。
- 左侧 Playable 始终为 `IntroToIdle`Clip 从 `Intro` 变为 `Idle`
- 右侧 `Direct Idle - Image` 立即显示 Idle 第 0 帧并持续循环。
- 右侧 Playable 和 Clip 均为 `Idle`Node 为空。
- 两个目标均不依赖 Animator 或 AnimatorController。
- Console 没有 Error 或 Exception。
### RT-02 长时间循环稳定性(P0)
操作:
1. 保持样例场景运行至少 60 秒。
2. 观察两个目标和 Inspector 中的帧索引。
预期:
- Idle 每约 4 秒循环一次。
- 不出现停播、闪空、越界帧或明显累计漂移。
- 内存和 Console 不持续产生异常或日志刷屏。
### RT-03 动态速度(P0
操作:
1. Play Mode 中将 Player 的 Speed 改为 `0`
2. 等待数秒,再改为 `0.5``2`,最后恢复 `1`
预期:
- Speed 为 `0` 时停在当前帧,但 State 仍为 Playing。
- 恢复正数后从当前进度继续,不从头重播。
- `0.5` 明显变慢,`2` 明显变快。
- 不丢失 Flow 节点或当前 playable。
### RT-04 Pause/Resume、Stop 和 Handle 语义(P0
操作:
1. 在 Test Runner 中运行第一阶段 EditMode 测试。
2. 重点查看 PlaybackSession、PlaybackHandle、Stop、Pause/Resume 和替换请求相关用例。
预期:
- Pause 不改变当前帧,Resume 从原进度继续。
- 新的合法 Play 将旧请求以 Replaced 结算。
- Stop 将活动请求以 Stopped 结算。
- 正常播完以 Completed 结算。
- 无效请求以 Failed 结算,且不会打断正在播放的合法请求。
- Task、协程和回调观察到同一个结果,每个请求只结算一次。
说明:当前样例 Inspector 只展示状态,没有完整运行时控制面板,因此这一组 API 语义以 Test Runner 的可重复结果作为验收依据。
### RT-05 Disable/Enable 生命周期(P0
操作:
1. Play Mode 中等待动画进入非首帧。
2. 禁用 `FrameAnimationPlayer` 组件。
3. 观察显示目标和 Runtime State。
4. 重新启用 Player。
5. 再对整个 GameObject 执行一次禁用和启用。
预期:
- 禁用时活动请求以 Stopped 结束。
- 当前 Sprite 保留,不自动 Clear 或 Hide。
- 重新启用时,因为 `playOnEnable = true`,默认 playable 从头播放。
- 左侧重新从 Intro 第 0 帧开始,右侧重新从 Idle 第 0 帧开始。
### RT-06 目标配置校验(P0
在临时场景或复制对象上执行,测试后不要保存改动。
| 配置 | 预期 |
|---|---|
| Player,无 SpriteRenderer/Image | Inspector 显示缺失目标错误,Play 返回 Failed |
| Player + SpriteRenderer + Image | Inspector 显示目标冲突,Play 返回 Failed |
| Player + 唯一目标,但 Graph 为空 | Inspector 显示 Graph 缺失,Play 返回 Failed |
| Speed 为负数 | Inspector 显示非法速度;运行时 SetSpeed 不接受负值 |
失败请求不得清空或替换其他对象上正在播放的合法动画。
### RT-07 Clear 与 HideTargetP1
操作:
1. 在 PlayMode Test Runner 中运行 Clear/HideTarget 相关用例。
2. 检查 SpriteRenderer 和 Image 两套目标的结果。
预期:
- ClearSprite 变为 null,但显示组件仍启用。
- HideTarget:只禁用 SpriteRenderer 或 Image。
- Hide 后再次成功 Play 会重新启用目标,并立即显示第 0 帧。
- SpriteRenderer 与 Image 语义一致。
## 4. 第二阶段:Aseprite 导入测试
### IMP-01 样例与中文数据(P0
操作:
1. 执行 `Rebuild Import Sample`
2. 选中 `ImportSampleGraph.asset`
3. 展开 Import Sources。
4. 点击 `Preview All Enabled`
预期:
- 存在 Object/只读切图和 Array/自动切图两个来源。
- internalId 是只读 GUID,并可复制。
- 中文来源名、Tag 和 frameName 没有乱码。
- 可看到 `待机``眨眼``转身``惊讶` 等 Imported Clip。
- 初始预览不出现 Error;成功构建后 Clip 状态应为 Unchanged。
- Imported Clip 显示为 Graph 的 sub-asset,而不是独立 `.asset`
### IMP-02 Object/Array 与 directionP0
操作:
1. 在 Test Runner 中运行第二阶段 Parser 和 TagExpansion 测试。
2. 查看 Object、Array 和四种 direction 用例。
预期:
- Object 和 Array 得到相同语义的 SourceFrame。
- Object 帧顺序保持 JSON 属性原始顺序,不按名称重新排序。
- `forward``reverse``pingpong``pingpong_reverse` 顺序正确。
- pingpong 不重复首尾端点,单帧 Tag 只产生一帧。
### IMP-03 普通内容更新与原地刷新(P0)
操作:
1. 在 Project 窗口展开 `ImportSampleGraph`,选中任意 Imported Clip并保持 Inspector 锁定。
2. 打开对应测试 JSON,将该 Tag 使用帧的 `duration` 改为另一个正整数。
3. 回到 Unity,等待 JSON 重新导入。
4. 点击对应来源的 `Preview Source`
5. 确认显示 SourceChanged Warning 和 Updated Clip。
6. 点击 `Refresh Source`
预期:
- Preview 不直接修改 Clip。
- Refresh 后仍是原来的 Clip sub-asset,锁定的引用不丢失。
- Clip 的帧时长更新。
- `displayName``speed``defaultEndBehavior` 不被覆盖。
- 刷新后再次 Preview,状态变为 Unchanged,来源变化 Warning 消失。
### IMP-04 用户重命名 Clip id 后刷新(P0)
操作:
1. 选择一个未作为默认 playable 的 Imported Clip。
2. 将 Clip `id``displayName``speed` 改为自定义值。
3. 修改其源 JSON 的 duration。
4. 对来源执行 Preview 和 Refresh。
预期:
- 刷新仍通过 `importSourceId + sourceTagName` 找到原 Clip。
- frames 被更新。
- 用户设置的 id、displayName、speed 和结束行为保持不变。
- 不会因为 Clip id 已改变而创建重复 Clip。
测试完成后执行 `Rebuild Import Sample` 恢复样例,避免重命名影响默认 playable 或 Flow 引用。
### IMP-05 Tag Missing 与恢复(P0
操作:
1. 从 Object 测试 JSON 的 `frameTags` 中暂时删除 `待机` 条目,不删除 frames。
2. 点击 `Preview Source`
3. 确认 `待机` 显示 Missing,再执行 Refresh。
4. 检查 Graph sub-assets。
5. 将原 Tag 完整恢复,再次 Preview 和 Refresh。
预期:
- Tag 消失时原 Clip 保留,只设置 `isMissingFromSource = true`
- 原 Clip 的 Sprite 和帧表不被自动删除。
- Missing Clip 在完整 Graph 校验中报告错误。
- Tag 恢复后更新同一个 Clip,并清除 Missing。
- Node、Flow 或其他对象对原 Clip 的引用不丢失。
### IMP-06 Tag 改名规则(P1
操作:
1. 将测试 JSON 中一个 Tag 改为全新的、不冲突的名称。
2. Preview 并 Refresh。
预期:
- 旧 Tag 对应 Clip 变为 Missing。
- 新 Tag 创建新的 Imported Clip sub-asset。
- 系统不猜测两者关联,也不迁移旧引用。
执行 `Rebuild Import Sample` 恢复样例。
### IMP-07 只读 Sprite 匹配(P0
操作:
1. 选择 Object/只读切图来源,确认 `manageSpriteSlicing = false`
2. 记录对应 PNG `.meta` 的 Git diff 状态。
3. Preview 和 Refresh 一次未变化的来源。
4. 将 JSON 中一个 frame rect 改成无法匹配已有 Sprite 的位置,再 Preview。
预期:
- 正常情况下按 frameName 和 rect 唯一匹配现有 Sprite。
- 未变化刷新不会修改 TextureImporter 或 PNG `.meta`
- rect 无法匹配时出现 SpriteMatchFailed Error。
- 系统不会自动改切图,也不会应用 Graph/Clip 修改。
### IMP-08 自动切图与稳定 Sprite IDP0
操作:
1. 选择 Array/自动切图来源,确认 `manageSpriteSlicing = true`
2. 将某个 Sprite 拖到临时场景中的 SpriteRenderer,形成真实序列化引用。
3. 修改 JSON 中该 frame 的 rect,但保持 frameName 不变且 rect 合法。
4. 点击 `Preview Source`
5. 检查 Sprite diff 后点击 `Refresh Source`,确认 TextureImporter 修改对话框。
预期:
- 差异明确显示 Updated SpriteRect。
- 必须确认后才修改 TextureImporter。
- 刷新后同名 Sprite 保持原 spriteID。
- 临时 SpriteRenderer 的 Sprite 引用不变,不出现 Missing。
- rect/pivot 更新为 JSON 和 ImportSource 设置。
- JSON 中消失的 frameName 对应旧 SpriteRect仍保留。
### IMP-08A 合并帧共享 SpriteP0
操作:
1. 使用包含多个 SourceFrame 指向相同 `frame.x/y/w/h` 的 Aseprite JSON。
2. 确认来源设置为 `manageSpriteSlicing = true`,点击 `Preview Source`
3. 检查来源摘要中的逻辑帧、SpriteSlot 和共享别名数量。
4. 确认刷新后检查对应 Imported Clip 的帧表和 Sprite引用。
5. 再次执行 Preview 和 Refresh。
佩佩素材的预期统计:
- 347 个逻辑 SourceFrame。
- 205 个唯一 SpriteSlot。
- 142 个共享别名。
预期:
- 相同 rect 只创建一个有效 SpriteRect。
- 不同 SourceFrame仍保留各自的 frameName、sourceIndex、duration 和 Tag顺序。
- 指向同一 rect 的 Clip Frame直接引用同一个 Unity Sprite。
- 第二次 Preview 中 SpriteSlot 和 Clip均显示 UnchangedSprite ID保持稳定。
- 原始 PNG 和 JSON不被修改。
### IMP-09 多写入所有者冲突(P0)
操作:
1. 在样例 Graph 中点击 `Add ImportSource`
2. 启用新来源,绑定自动切图来源正在使用的同一 Texture 和任意有效 JSON。
3. 设置 `manageSpriteSlicing = true`
4. 点击 `Preview All Enabled`
预期:
- 出现 TextureOwnershipConflict Error,并列出冲突来源。
- Refresh 不修改 TextureImporter、Graph 或 Clip。
- 将新增来源禁用后冲突消失。
测试完成后执行 `Rebuild Import Sample` 清理额外来源。
### IMP-10 阻断错误与单来源原子性(P0)
依次测试以下任一错误:
- duration 改为 `0`
- frame rect 越出 Texture。
- `trimmed = true`
- `rotated = true`
- direction 改为未知值。
- Tag 范围越界。
操作:
1. 先记录当前 Clip 帧表和 Texture `.meta`
2. 制造错误并点击 `Preview Source`
3. 再点击 `Refresh Source`
预期:
- Preview 显示具体结构化 Error。
- 不出现部分 Clip 更新。
- TextureImporter 和 `.meta` 不变化。
- `lastSourceHash` 不更新。
- 现有 Clip 和 sub-asset 引用保持原样。
### IMP-11 全部来源原子性(P0
操作:
1. 在一个来源中修改合法 duration,使其产生 Updated。
2. 在另一个来源中制造 duration 为 `0` 的阻断错误。
3. 点击 `Preview All Enabled`,然后点击 `Refresh All Enabled`
预期:
- 预览同时显示合法变化和阻断错误。
- 因任一启用来源失败,整批刷新不应用。
- 合法来源的 Clip 也保持刷新前状态。
- 两个来源的 hash 均不更新。
### IMP-12 命名冲突(P1
分别制造:
- 两个启用 ImportSource 使用同名 Tag。
- 新 Tag 与现有 Clip id 同名。
- 新 Tag 与现有 Flow id 同名。
预期:
- Preview 显示 TagConflict 或 PlayableIdConflict。
- 错误信息能够指出来源和冲突名称。
- 系统不自动加前缀、不自动改名、不创建部分 Clip。
### IMP-13 Undo/Redo 与保存稳定性(P1
操作:
1. 执行一次成功的 duration 刷新。
2. 使用 Undo,检查 Clip 帧表和来源 hash。
3. 使用 Redo,再次检查。
4. 保存项目,关闭并重新打开 Unity。
5. 重新检查 Graph、Imported Clip、ImportInfo 和中文名称。
预期:
- Undo/Redo 对 Graph 和 Clip 修改成组生效。
- 新建 sub-asset 不留下孤立对象。
- 重启后中文名称、sourceTagName、internalId 和引用不变化。
- 再次 Preview 能正确判断当前来源是否已刷新。
## 5. 自动测试回归
手动测试完成前至少执行:
1. Test Runner > EditMode > Run All。
2. Test Runner > PlayMode > Run All。
通过标准:
- 第一阶段既有测试全部通过。
- 第二阶段 Parser、direction、只读匹配、稳定 sub-asset、Missing 和所有权测试全部通过。
- 没有新增 Console Error 或未处理 Exception。
## 6. 总体验收标准
以下项目全部满足后,第一、二阶段可视为通过:
- 不依赖 AnimatorController 即可播放直接 Clip 和线性 Flow。
- SpriteRenderer 与 Image 的首帧、循环、速度、生命周期和显示语义正确。
- 播放请求完成原因和 Failed 隔离符合定义。
- Object/Array Aseprite JSON 均能正确解析,中文数据稳定。
- Imported Clip 能新增、原地更新、Missing 和恢复。
- 用户拥有字段不会被刷新覆盖。
- 只读来源绝不修改 TextureImporter。
- 自动切图为同名 frameName 保留稳定 Sprite ID。
- 合并帧只创建唯一 SpriteSlot,同时保留全部逻辑帧时长与播放顺序。
- 多写入所有者、素材错误和命名冲突能够阻断刷新。
- 单来源和全部来源刷新都不产生部分 Graph/Clip 更新。
- 保存、Undo/Redo 和重新打开 Unity 后资产引用保持稳定。
若任一 P0 用例失败,先停止进入第三阶段并记录缺陷;P1 问题可以评估后决定是否阻断,但不得破坏资产引用、原子刷新或运行时播放语义。