美文网首页
[Unity] Playables学习整理 - 2 (Timel

[Unity] Playables学习整理 - 2 (Timel

作者: _Walker__ | 来源:发表于2023-04-13 12:03 被阅读0次

环境

  • Unity 2020、Unity 2021

1、TML对象(Playable)的创建过程

时序图

1.1

  个人推测,整个创建过程是由PlayableDirector发起的,依据如下:

  • Play前要先设置好playableAsset
  • playableAsset是直接存储在Navitve层的(也可以序列化存储起来直接用)
/*PlayableDirector反编译的代码*/

public PlayableAsset playableAsset
{
  get => this.Internal_GetPlayableAsset() as PlayableAsset;
  set => this.SetPlayableAsset((ScriptableObject) value);
}

public void Play(PlayableAsset asset)
{
  if ((UnityEngine.Object) asset == (UnityEngine.Object) null)
    throw new ArgumentNullException(nameof (asset));
  this.Play(asset, this.extrapolationMode);
}

public void Play(PlayableAsset asset, DirectorWrapMode mode)
{
  this.playableAsset = !((UnityEngine.Object) asset == (UnityEngine.Object) null) ? asset : throw new ArgumentNullException(nameof (asset));
  this.extrapolationMode = mode;
  this.Play();
}

[NativeThrows]
[MethodImpl(MethodImplOptions.InternalCall)]
public extern void Play();

1.2

  通过上面的时序图,可以大致看出TimelineAsset、Track、Clip、Mixer、Playable这些元素间的关系。

  • TimelineAsset是一个容器,它记录了Track、Clip的信息,并且会根据这些信息创建它们相关的Playable
  • TimelineAsset本身会创建一个Playable(ScriptPlayable<TimelinePlayable>),它是根Playable,由它关联着各个Track、Clip对应的Playable
  • TrackAsset负责创建Mixer的Playable,在public virtual Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)中实现。同时也一定程度上管理Clip的创建。
  • XxxClipAsset负责最终的Clip Playable创建,在Playable CreatePlayable(PlayableGraph graph, GameObject owner)里实现。
    有个小点值得注意,与Track不同Clip的Asset并没有一个专用的基类,它是直接继承自PlayableAsset。这么做大概是因为它足够简单,只是一个用来创建Clip Playable的小工厂,没有其他的管理职责。
  • RuntimeClip、TimelineClip这两个类在结构上很重要,但是跟创建Playable没太大关系。放在时序图里,是为了表明它们出场的时机,更多的介绍在下面结构部分。

2、TML的(类)结构

TML类图-Asset部分 TML类图-Clip部分

  为了理解TML的机制,以及TML跟Playable的关系,下面几个类需要重点关注:RuntimeClipTimelineClipTimelineAssetTrackAssetTimelinePlayable 这些类可以大致分为三组:

  • 序列化存储:TimelineAsset、TrackAsset、TimelineClip、XxxClipAsset(类图中没有)
  • 播放(时间)控制:RuntimeClip、其他IInterval的实现类
  • Playable结构:TimelinePlayable

1)TimelineAsset

  • 它继承自PlayableAsset,所以会创建一个自己的Playable
  • 它是一个ScriptableObject,所以它用于序列化存储的信息
  • m_Tracks记录了这一个TML里所有的一级(Root)TrackAsset信息。也就是在【Timeline】视图中,把所有轨道折叠起来后,最外层的部分。
    GroupTrack - 只有分组的作用
    PlayableTrack - 有实际功能的轨道
    自定义的TrackAsset类 - 实现自定义轨道时,可以继承TrackAsset记录自己的轨道信息
  • IEnumerable<TrackAsset> GetOutputTracks() 取有Output的TrackAsset,也就是有实际功能的轨道Asset。
    在Unity实现中,是在所有的TrackAsset中里把GroupTrack排除掉之后,返回结果。
  • public override Playable CreatePlayable(PlayableGraph graph, GameObject go) 代码注释写的是 “Creates an instance of the timeline”,对返回值的说明是 “The Root Playable of the Timeline”
    从代码中看到,它就是创建了一个ScriptPlayable<TimelinePlayable>的实例,并将所有轨道的输入、输出关联到这个实例上。关联关系见下图:
    TML的Graph图

  这里我有个很疑惑的点,TimelinePlayable的输出只有一个(playable.GetOutputCount()=0),但它却关联了多个PlayableOutput的实例。从图中能看到,它应该是通过“SourceOutputPort”来区分的,但这个Port的概念是什么无从得知。从PlayableTraversalMode.Passthrough这个枚举值的注释中可以推测:OutputPort跟InputPort是一一对应的,若[SourceOutputPort=3],它的输入应该是,TimelinePlayable中[Input 3]对应的Playable。

Causes the Playable to act as a passthrough for PrepareFrame and ProcessFrame.
If the PlayableOutput being processed is connected to the n-th input port of the Playable, the Playable only propagates the n-th output port.
Use this enum value in conjunction with PlayableOutput SetSourceOutputPort.

这种只有一个Output却产生多个PlayableOutput结果的设定,让我很不解,如果有大佬明白这里的设计思路,还请不吝赐教!!!!

2)TrackAsset

  • 在序列化存储、Playable创建这两方面,它跟TimelineAsset差不多,只是它只负责轨道相关的部分
  • 在Timelin的设计中,轨道、分组都是Track,所以能看到它有两个非常简单的子类:PlayableTrack、GroupTrack
  • 观察YAML序列化文件发现
    m_Children是GroupTrack在用,它记录着分组下的所有TrackAsset引用;
    m_Clips是PlayableTrack(含自定义的TrackAsset)在用,它记录每个轨道的Clip引用。
  • 特别要注意,m_Clips记录的元素对象是TimelineClip并不是ClipAsset
  • startend提供轨道的开始、结束时间,这两个时间并不是序列化存储下来的,而是每次取的时候根据所有Clip的起止时间计算得到。
    Unity通过CombineHash对比,规避了每次都算时间的消耗,但每次算Hash也是有开销的。
  • 子类可以重载public virtual Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)创建自定义的Mixer Playable
  • 在自定义Timeline轨道的时候,TrackAsset的主要用途就是存储轨道的数据,并创建自定义的Mixer Playable实例

3)TimelineClip

  • 它只是一个记录Clip时间数据的C#类(Serializable),并不是ClipAsset


    TimelineClip主要记录的信息
  • 同时它也负责处理各种时间的转换,如double ToLocalTime(double time)根据TimelinePlayable的当前时间,计算出Clip自身的当前时间。TimelinePlayable的当前时间,应该等于整个TML的当前播放时间,因为没有地方改变它的播放进度。
  • 真正的ClipAsset由Object m_Asset引用着,通常它的使用方式非常简单,就是创建一个Playable (clip.asset as IPlayableAsset).CreatePlayable(graph, gameObject);。当然,也可以转型为具体的ClipAsset,在里面取一些特定信息。
  • 在自定义Timeline轨道的时候,ClipAsset的主要用途是存储Clip相关的数据,并创建自定义的Clip Playable实例

4)RuntimeClip

  • 它在每次Evaluate的时候,计算当前Clip的时间,并更新到Playable
  • 它在每次Evaluate的时候,根据融合参数,计算当前Clip在父级Mixer中的权重

5)TimelinePlayable

  • 它是一个PlayableBehaviour
  • 它的主要作用是在PlayableGraph播放的过程中,调度TML相关Playable(主要是Clip)的播放进度
  • m_CurrentListOfActiveClips是每次PrepareFrame取到的,当前时刻处于激活状态的Clip列表。通常它里面的元素是RuntimeClip
  • m_ActiveClipsm_CurrentListOfActiveClips的副本,用于在下次执行时,判断哪些Clip应该被禁用(Disable)
  • m_PlayableCachem_IntervalTree这两个字段里分别持有TrackAsset、ClipAsset的信息,在调试的时候可以通过反射的方式,在它们里面找到需要的信息。
    (我在调试TML的时候,多次想要在Mixer里输出一些Asset的数据,发现当前对象是PlayableBehaviour的实例,根本访问不到Asset的内容)

相关文章

网友评论

      本文标题:[Unity] Playables学习整理 - 2 (Timel

      本文链接:https://www.haomeiwen.com/subject/sbewddtx.html