如何迁移uGUI到UI Toolkit并保留动画

解读

国内项目普遍从 Unity 2019 LTS 起步,大量线上产品仍依赖 uGUI。近两年,字节、米哈游、叠纸 等厂商在新预研项目中开始试点 UI Toolkit,以解决多分辨率、热更新与图集冗余问题。面试官问“迁移并保留动画”,核心想验证三点:

  1. 你是否真正踩过混合 UI 的坑(输入事件、渲染顺序、图集割裂);
  2. 对 UI Toolkit 动画系统(USS Transition、UIAnimation 轨道、VisualElement 生命周期)的理解深度;
  3. 能否给出可落地的灰度方案,而不是“一键批量转换”这种理想答案。
    回答时务必体现“存量资产无损 + 增量模块可热更 + 性能不掉”的三角约束。

知识点

  1. uGUI 动画本质:依赖 RectTransform + Animator,关键帧数据保存在 .anim 文件,曲线驱动的是 RectTransform 的 anchoredPosition/sizeDelta/scale/alpha。
  2. UI Toolkit 动画两条路
    • USS Transition:在样式表里声明 transition-property、duration、easing,只能做线性插值,无法复用旧动画曲线
    • UIAnimation 轨道:2022.2 新增,支持贝塞尔曲线,可绑定到 VisualElement.transform、style.opacity、style.translate,但轨道格式与 .anim 不兼容
  3. 数据映射鸿沟:uGUI 的 ColorBlock、SpriteSwap、AnimationTriggers 在 UI Toolkit 里对应的是 :hover、:active、:checked 伪类与 USS 自定义属性,事件名与状态机完全对不上
  4. 运行时双栈共存风险
    • InputSystem 同时派发 EventSystem.RaycastAll 与 UI Toolkit.Panel.Pick,点击穿透
    • UI Toolkit 默认使用 Persistent Atlas,与 SpriteAtlas v2 的 图集内存双份
    • 2021.3 之前版本,UI Toolkit 在 Android 上无 SRP Batcher 支持,DrawCall 可能暴涨。
  5. 国内热更新约束:Lua 或 ILRuntime 热更框架无法直接访问 UI Toolkit 的 VisualElement 类型,需要反射包装或代码生成,否则动画参数无法热修。

答案

我去年在 XX 项目负责把 300+ 个 uGUI 面板迁移到 UI Toolkit,最终保留动画的策略分四步,线上验证 0 崩溃、内存-18%、帧时间-22%

  1. 资产审计与分级
    用脚本扫描所有 .anim,把动画拆成三类:

    • A 类:仅插值 RectTransform 的 Pos/Size/Alpha,占比 70%;
    • B 类:包含 Image.color、CanvasGroup.alpha,占比 25%;
    • C 类:自定义曲线驱动材质参数或粒子,占比 5%。
      只对 A、B 类做自动转换,C 类暂时保留 uGUI。
  2. 动画曲线无损迁移
    写了一个 Editor 工具,把 .anim 中的 FloatCurve 读出,按帧采样 60 fps 生成 UIAnimation 轨道 JSON(2022.2 支持的格式),同时把曲线归一化到 [0,1] 区间,避免 UI Toolkit 的 style.translate 单位不一致问题。
    对于 ColorBlock 的渐变,把 AnimationCurve 转成 USS 的 transition: background-color 0.3s ease-in-out,并在 C# 侧用 element.AddTransition(new StyleValues{ backgroundColor = targetColor }, 300) 触发,保证与旧逻辑代码零耦合

  3. 双栈渲染隔离
    在 Camera 上挂两个 Canvas:

    • Overlay Camera(depth=1)只渲染 UI Toolkit Panel,RenderMode=ScreenSpace-Camera,SortingLayer=UIToolkit;
    • Legacy Camera(depth=0)渲染原 uGUI,SortingLayer=UGUI。
      Custom Frame Timing 把 UI Toolkit 的渲染放到 Legacy 之后,解决 Android 老机型上 UI Toolkit 与 SpriteAtlas 的图集冲突。
      同时把 EventSystem.sendNavigationEvents 关闭,用 UI Toolkit 的 Panel.EventDispatcher 接管导航,防止点击穿透。
  4. 灰度热更方案
    把 UI Toolkit 的 UXML/USS 放到 Addressable 分组,用 Hash 比对实现增量下载;动画参数通过 ScriptableObject 包装,走 Lua 热更框架的反射通道,运行时动态设置 VisualElement.style.translate,实测 4G 网络下首包增加 <200 KB。
    上线后按渠道灰度:TapTap 先放 10%,崩溃率 0.03%,低于 uGUI 版本的 0.05%,全量推送。

如果面试官追问“如何把 Animator 状态机一并迁移”,我会补充:
目前 UI Toolkit 没有官方状态机,但可以用 VisualElement 的 data-binding + Custom PropertyDrawer 把 AnimatorController 的 State 名映射到 USS 的 .state-open.state-close 类名,在 C# 侧手动驱动状态切换,这样策划之前配的 Animator 参数表可以继续用,无需改 Excel。

拓展思考

  1. Unity 2023 LTS 路线图中已明确 UI Toolkit 将支持 Timeline 轨道,届时可直接拖 .anim 到 Timeline,与 uGUI 动画共用一套资源,迁移成本会再降 50%。
  2. 国内小游戏平台(抖音、微信)对 UI Toolkit 的支持仍不完整,WebGL 后端在 iOS 15 以下有 SVG 滤镜兼容问题,需要 fallback 到 SpriteRenderer+CommandBuffer 做合批,否则 DrawCall 会飙到 200+。
  3. UI Toolkit 的 StyleSheet 变量系统(var())与美术的 Figma 插件对接后,可以做到“设计 token 一键导出 USS”,未来策划改颜色无需客户端发包,真正的 DDL 级热更,这是 uGUI 永远无法做到的。