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

15 KiB
Raw Blame History

帧动画系统第一、二阶段手动测试方案

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 始终为 IntroToIdleClip 从 Intro 变为 Idle
  • 右侧 Direct Idle - Image 立即显示 Idle 第 0 帧并持续循环。
  • 右侧 Playable 和 Clip 均为 IdleNode 为空。
  • 两个目标均不依赖 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.52,最后恢复 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 属性原始顺序,不按名称重新排序。
  • forwardreversepingpongpingpong_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 的帧时长更新。
  • displayNamespeeddefaultEndBehavior 不被覆盖。
  • 刷新后再次 Preview,状态变为 Unchanged,来源变化 Warning 消失。

IMP-04 用户重命名 Clip id 后刷新(P0)

操作:

  1. 选择一个未作为默认 playable 的 Imported Clip。
  2. 将 Clip iddisplayNamespeed 改为自定义值。
  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 问题可以评估后决定是否阻断,但不得破坏资产引用、原子刷新或运行时播放语义。