Unity Timeline 动态绑定与资源制作流程

项目里的 Timeline 越来越多之后,主场景也跟着引用了越来越多的动画和表现资源。美术制作时,把 Timeline 挂到场景中的 PlayableDirector 上很方便;但如果把这份引用关系一直保留到运行时,场景就会持续携带这些资源依赖。

这次调整把 Timeline 资源与场景对象的绑定信息单独保存,需要播放时再加载并恢复绑定。美术仍然在场景中制作和预览,交付时多一个导出步骤,运行时则按实际需要装配。

TimelineAsset 与绑定信息是两部分

TimelineAsset 保存轨道、片段和时间等内容,但一条轨道究竟控制场景中的哪个对象,通常由 PlayableDirector 保存。

例如一个 AnimationTrack 需要绑定 Animator,一个 ActivationTrack 需要绑定 GameObject。把 TimelineAsset 从 Director 上取下来再动态加载回来,并不会凭空知道之前拖进去的是哪一个场景对象。

因此需要一起解决两件事:资源如何按需加载,以及加载后如何恢复轨道目标。

制作阶段导出 Timeline 绑定,运行时加载资源并通过场景索引恢复轨道引用

动态绑定能够切断场景到部分资源的直接引用链,但不自动保证首包一定减少多少。最终打包结果还取决于资源分组和其他引用是否仍然存在,不能只根据场景 Inspector 已经清空就判断收益。

导出时保存哪些内容

我为每个 Timeline 保存一份绑定描述,记录资源信息与各条轨道的目标:

字段 用途
Timeline 资源路径与 GUID 编辑器定位与对应关系检查
轨道名称、轨道类型 找回待绑定的轨道
目标对象的层级路径 在场景或指定根节点下定位对象
目标对象名称 辅助诊断与受约束的查找
对象类型、组件类型 区分绑定 GameObject 还是某个组件

数据本身采用 XML 保存。关键不在 XML 这个格式,而在于文件保存的是可恢复的定位信息,没有继续持有原场景对象的直接引用。

下面是一条经过简化的描述:

1
2
3
4
5
6
<TrackBindingData>
<TrackName>Character</TrackName>
<TrackType>AnimationTrack</TrackType>
<BoundObjectPath>Stage/Actor</BoundObjectPath>
<BoundComponentType>Animator</BoundComponentType>
</TrackBindingData>

导出器从 timeline.outputs 遍历轨道,通过 director.GetGenericBinding(output.sourceObject) 读取当前绑定。如果目标是组件,就保存它所在对象的路径和组件类型;如果目标是 GameObject,就保存对象本身的类型。

文件成功写出之后,再清除 Director 的轨道绑定和 playableAsset。编辑器侧同时记录 Undo 并标记对象变更,避免用户看到场景清空时无法撤销。

运行时怎样找回引用

播放前先加载 TimelineAsset 和绑定描述,给 Director 设置资源,再把描述中的目标恢复成对象引用。核心调用仍然是 Unity 自带的 API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
director.playableAsset = timeline;

foreach (TrackAsset track in timeline.GetOutputTracks())
{
if (!bindings.TryGetValue(track.name, out var binding))
continue;

UnityEngine.Object target = ResolveTarget(binding, sceneRoot);
if (target == null)
{
ReportMissingBinding(track.name, binding);
continue;
}

director.SetGenericBinding(track, target);
}

这是绑定流程的示意,资源加载、失败汇总和是否允许播放由外层管理。一个关键轨道没有绑上时,不能只打一条日志就当成播放准备成功。

我在当前版本里按轨道名称建立查找表,所以同一份资源中的输出轨道名称需要保持唯一,重命名之后也要重新导出。如果要支持任意重名轨道,就需要改用更稳定的标识,不能继续让同名项覆盖。

这个工具处理的是轨道的 Generic Binding。片段内部的 ExposedReference 等引用有自己的解析机制,不能假设导出轨道绑定就已经覆盖了 Timeline 中的所有依赖。

建立场景对象索引

如果每条轨道都从场景根节点重新搜索一次,多个 Timeline 依次播放时就会重复遍历相同层级。我把场景对象按路径建立索引,后续绑定直接查找;名称索引则保留同名对象集合,便于处理辅助查询。

查找范围可以限定在一个根节点下,也可以遍历已经加载的场景。使用哪一种方式,需要与导出路径的约定一致,尤其不能把完整场景路径与相对路径混用。

缓存也需要失效条件。场景或根节点切换时可以重建,但同一个场景内临时生成、销毁和重命名对象,也可能让缓存过期。这种变化不能单靠场景名称或场景句柄检测,相关流程需要显式清理或刷新索引。

路径绑定的优点是数据直观、容易诊断,代价是依赖层级稳定。目标节点移动之后,编辑器预览还正常,旧绑定文件却可能失效,所以重新保存绑定必须成为资源修改流程的一部分。

美术制作流程怎样保留

新的流程没有改变 Timeline 的基本编辑方式,主要是在保存与接入之间增加导出和回读:

  1. 在合适的场景中,把 TimelineAsset 挂到一个制作使用的 Director 上。
  2. 选中场景中的 Director,绑定目标对象并录制动画。
  3. 预览效果,先保存 Timeline 中修改的动画资源。
  4. 从 Director 的菜单导出绑定信息。
  5. 确认绑定文件生成,Director 的引用被清除,再保存场景。
  6. 需要继续修改时,重新挂载 Timeline 并读取绑定文件,恢复编辑状态。

“点击 Project 里的 TimelineAsset”与“选中场景里的 Director”不是同一个入口。需要录制场景对象时,使用的是后者,否则绑定或录制功能可能不可用。

也不是所有 Timeline 都需要额外文件。如果完整演出都放在一个预制体内,并且没有引用预制体外的场景对象,可以保留预制体内部的绑定关系,直接加载预制体。额外的绑定描述主要用于跨越资源与场景边界的情况。

播放结束后要归还控制权

资源能够加载、轨道能够绑定,只说明演出能够开始。相机等对象在结束后是否恢复,同样需要进入制作验收。

当时的相机制作约定要求:演出最后一帧不再保持 Timeline 专用相机的激活状态,避免正常结束后仍占用视角。实际项目还应核对 ActivationTrack 的结束设置,以及相机系统自身的优先级规则。

如果允许中途打断,单靠时间轴最后一帧还不够。外层停止流程也需要处理相机归还、Director 清理和动态资源释放,避免把“自然播完”的收尾误当成所有退出路径。

接入时检查完整往返流程

这次工具完成后,我把保存、回读和运行时加载放进同一份制作说明,并要求原有 Timeline 的播放入口做回归。新功能能播,并不能证明旧资源在拆开引用之后仍然完整。

对一份准备交付的资源,我会至少走一遍“制作 → 导出 → 重新读取 → 运行时播放 → 结束或打断”。这样既能发现路径和轨道名失配,也能发现动画未保存、相机未释放等制作问题。

动态绑定的价值,是让资源在制作时保持可见、可编辑,在运行时又能够独立加载。代码与制作约定必须一起交付,这条路径才能长期维持。

参考:PlayableDirector.GetGenericBinding、PlayableDirector.SetGenericBinding。