Timeline|01 基础介绍
四大脚本
-
Data - PlayerBehaviour 每一个Clip所包含的数据,属于Clip
- Clip - PlayerAsset 轨道上的每一个片段
- Mixer - PlayerBehaviour 可以理解为一个总的Clip,用于混合所有的子Clip
- Track - TrackAsset 轨道,用于存放Clip
自定义轨道
以物品移动为例创建一个简单的自定义轨道
创建Data
这代表我们Clip里所需要用到的数据
[System.Serializable]
public class TransformTweenBehaviour : PlayableBehaviour
{
public Transform endLocation;
}
创建Clip
[System.Serializable]
public class TransformClip : PlayableAsset, ITimelineClipAsset
{
// data
public TransformTweenBehaviour data = new TransformTweenBehaviour();
public ExposedReference<Transform> endLocation;
public ClipCaps clipCaps => ClipCaps.Blending;
// Factory method that generates a playable based on this asset
public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
{
var playable = ScriptPlayable<TransformTweenBehaviour>.Create(graph, data);
var clone = clip.GetBehaviour();
clone.endLocation = endLocation.Resolve(graph.GetResolver());
return playable;
}
}
首先ITimelineClipAsset是Clip的接口,继承之后会有一个clipCaps的属性可以重写,他影响面板上Clip的样式,不过这不重要
Data是属于Clip的
所以我们的Clip里很自然的会定义一个所需要的Data数据,也就是之前的TransformTweenBehaviour
重点是这个CreatePlayable方法,方法啥时候调用呢?为什么是叫Playable而不是Clip呢?
Clip只是一种资源形势,而我们运行Timeline的第一帧,系统会调用这个方法,把Clip转换为Playable,同时塞入Data数据
创建Playable的方法是固定的,照着写就行了
var playable = ScriptPlayable<TransformTweenBehaviour>.Create(graph, data);
这是一个工厂方法,graph就是图,也就是整个Timeline,这个我们不管
Create方法还要求我们塞入Clip所需要的数据类型,他会把数据类型给我们填到Playable中
但是这时候Playable里还是没有真实数据的,只有数据类型而已
所以我们还需要把数据类型自己赋值进去
var clone = clip.GetBehaviour();
clone.endLocation = endLocation.Resolve(graph.GetResolver());
那么这里的Resolve又是啥玩意呢
因为Clip是一种资源,资源是不能直接记录资源引用的,Mono可以引用Asset,但是反过来Asset是不能引用Mono的,所以就需要用到一种奇怪的方式
定义成如下结构
public ExposedReference<Transform> endLocation
然后想要查找资源的时候再使用Resolve解析就好
创建Track
基础部分,分别代表颜色,Track里的Clip类型,Track自身能够绑定的资源类型
[TrackColor(0.855f,0.855f,0.855f)]
[TrackClipType(typeof(TransformTweenClip))]
[TrackBindingType(typeof(Transform))]
public class TransformTweenTrack : TrackAsset
{
}
给Data添加行为
数据和行为,都会在Data里,所以Data才叫做PlayableBehaviour
可以发现Data里我们可以重写很多生命周期方法
我们重写其中的ProcessFrame,很明显是一个每帧都会调用的方法
[System.Serializable]
public class TransformTweenBehaviour : PlayableBehaviour
{
public Transform endLocation;
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
var actor = playerData as Transform;
if (actor == null)
return;
actor.position = Vector3.Lerp(actor.position, endLocation.position, info.deltaTime);
}
}
预览
首先我们需要一个Playable Director
随便创建一个Timeline然后拖进去
这里是我们需要移动的目标物体
表面上Cube是绑定在Track上的,实则不然,我们观察一下Playable Director就可以发现问题
所以资源实际上还是记录在了Mono上
点击Clip我们可以选择我们的EndLocation
点击播放
但实际上目前的移动逻辑是有一定的问题的
混合轨道
MixerPlayable本质上他也是一个Clip,但是他就特殊在,他是铺满整个轨道的一个Playable,因此他可以在任何时候对轨道进行处理(所谓混合也就是在恰当的逻辑时间点进行逻辑处理而已
Mixer的逻辑很蠢,他会在每一帧遍历所有的Clip,然后会你可以获取每一个Clip在当前帧的权重,根据这权重值,你可以自己进行逻辑处理
比如时刻A,Clip2的权重就是1,而Clip、Clip3的权重都是0
而时刻B,Clip的权重为0,Clip2、Clip3的权重可能都是0.5
Mixer只是把权重你告诉你,至于具体你想怎么混合,那是你自己的事情
修改移动行为
创建混合轨道前,我们先给移动行为添加一些属性
一个新的Duration
public class TransformTweenClip : PlayableAsset, ITimelineClipAsset
{
[NonSerialized]
public float duration;
public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
{
...
clone.duration = duration;
...
}
}
每帧逻辑也要修改,GetTime获取的是当前Playable的运行时间,是当前这个Clip的,每一个Clip都会从0开始,最长时间等于duration
[System.Serializable]
public class TransformTweenBehaviour : PlayableBehaviour
{
public Transform endLocation;
public float duration;
public float weight;
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
var actor = playerData as Transform;
if (actor == null)
return;
actor.position = Vector3.Lerp(actor.position, endLocation.position, info.deltaTime * weight);
}
}
创建混合轨道
他本质上也是一个Clip,所以也需要创建一个数据(Behaviour)
public class TransformTweenMixerBehaviour : PlayableBehaviour
{
}
但这次,我们是在Track里面创建他,固定写法,同时,在创建时,我们给所有的Clip的Duration赋值
[TrackColor(0.855f,0.855f,0.855f)]
[TrackClipType(typeof(TransformTweenClip))]
[TrackBindingType(typeof(Transform))]
public class TransformTweenTrack : TrackAsset
{
public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
{
foreach (var clip in GetClips())
{
var clipAsset = clip.asset as TransformTweenClip;
if (clipAsset != null)
clipAsset.duration = (float) clip.duration;
}
return ScriptPlayable<TransformTweenMixerBehaviour>.Create(graph, inputCount);
}
}
由此不难看出Mixer是优先于Playable创建的
然后修改Mixer的逻辑
public class TransformTweenMixerBehaviour : PlayableBehaviour
{
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
var transform = playerData as Transform;
if (transform == null)
return;
// 一共有几个Clip
var count = playable.GetInputCount();
// 每一个Clip的权重
for (int i = 0; i < count; i++)
{
var weight = playable.GetInputWeight(i);
var inputPlayable = (ScriptPlayable<TransformTweenBehaviour>) playable.GetInput(i);
var behaviour = inputPlayable.GetBehaviour();
if (behaviour != null)
behaviour.weight = weight;
}
}
}