TimeLine编辑器可以用来编辑游戏内剧情动画,可以添加模型、动画、声音和Prefab等常用组件。

拖动时间线,可以编辑它们的Transform信息,控制动画的播放与关闭、模型的隐藏或显示。此外,还可以动态创建Prefab。另外,它还提供了自定义脚本来拓展编辑内容,这样将大幅度提高编辑的灵活度。

创建Timeline

在Project视图中,选择Create——Timeline命令即可创建Timeline。可见timelines是一个Assets,一个asset就需要scene中有相应的能接受这个asset的instance(实例)的component。(就像我们的animationclip和animator controller都需要在animator组件当中引用)

创建Timeline

在hierarchy中添加一个GameObject,添加Playable director组件,将创建的timeline资源拖入Playable中。这样的话,我们点击此GameObject,timeline窗口就能自动响应(Windows——Sequence——timeline)。Update Method表示时间线更新的方法,play on Awake表示是否运行就启动时间线,Wrap Mode设置循环模式,Initial Time表示时间线起始时间。Playable Director作为时间线的总控制组件,也提供了Play()Pause()Stop()等控制类方法

Playable Director

在Timeline窗口中,我们可以新建各种Track

timeline窗口

  • Track Group:可将多轨迹分组,方便管理。
  • Activation Track:激活轨道,将游戏对象拖进来就能在时间线中激活或关闭对象
  • Animation Track:动画轨道,首选需要绑定Animator组件,接着在inspector中编辑动画的信息。
  • Audio Track:声音轨道,需要绑定音频文件,在时间线中设置声音
  • Control Track:控制轨道。需要绑定prefab,它可以在时间线中动态实例化并且挂在某个节点下,例如特效。
  • Playable Track:可拓展轨道。可以挂载自定义的playable脚本。更加灵活地拓展。

Activation Track

使用Activation,可以控制游戏对象在时间线中的激活与关闭。创建轨道,在里面绑定一个GameObject,然后在右边窗口中右键——Add Activation Clip。可以添加多个,然后在时间线中设置激活时间和时长即可。

添加activation clip

如果时间线没有设置循环模式,那么它是有结束状态的。即PostPlaybackState,其中Active表示激活,Inactive表示隐藏,Revert表示还原初始状态,Leave as is表示保留最后的状态。(书中可以通过时间线在编辑器中设置,暂时还未找到)

PostPlaybackState

Animation Track

Animation Track需要绑定Animator。绑定后在右面窗口中右键——Add From Animation Clip命令,就可以添加此Animator内的动画Clip,可以像剪辑软件一样给动画设置缓入缓出,出点入点以及分割等。Add From Animation Playable Asset表示可拓展状态资源(这个选项只在书中的Unity出现),Add Override Track表示还可以添加一个Override层,它和前面讲过的Avatar Mask一样,可以添加遮罩,以禁止部分动画。注意Override Track遵循Unity的层逻辑,最下方的层覆盖上方的层,这一点要和普通剪辑软件的逻辑区分开。

添加Animation Clip

不论是Animation Track还是Override Track都可以应用Avatar Mask

选中后的inspector面板

Audio Track

Audio Track需要绑定Audio Source组件,绑定后在右面窗口中右键——Add From Audio Clip命令,就可以添加相应的 Audio Clip,还可以设置它是否循环播放

添加Audio Clip

Control Track

Control Track可以在时间线上动态创建Prefab。Parent Object表示将Prefab创建在这个游戏对象下面,Prefab表示它的源文件,其他操作和Track类似

添加GameObject

对应的inspector

Playable Track

Playable Track表示可编程型时间线,它提供了自定义时间线的数据以及更新周期。一个timeline本质上就是一个playable,

我们可以像创建脚本一样,在Project内创建Playable Behaviour以及Playable Asset。其中Playable Asset可以在Timeline中引用

创建Playable脚本

PlayableAsset脚本与Timeline引用

在PlayableAsset脚本中,可以添加序列化对象,此对象在编辑器中编辑然后反序列化后,在Asset脚本中只作为资源,需要再传递给Playable Behaviour脚本中产生行为

我们先在NewPlayableBehaviour脚本中输入下面代码,PlayableBehaviour表示了时间线上每个资源的生命周期,脚本可以用在多个不同的资源上,因为多个不同的playableAsset可以添加多个PlayableBehaviour脚本的实例。它的执行顺序是:OnGraphStart()OnBehaviourPause()OnBehaviourPlay()OnBehaviourPause()OnGraphStop(),其中PrepareFrame()表示每帧都会执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;

// A behaviour that is attached to a playable
public class NewPlayableBehaviour : PlayableBehaviour
{
//接收序列化的对象
public GameObject exposedObjectValue;
// Called when the owning graph starts playing
public override void OnGraphStart(Playable playable)
{

}

// Called when the owning graph stops playing
public override void OnGraphStop(Playable playable)
{

}

// Called when the state of the playable is set to Play
public override void OnBehaviourPlay(Playable playable, FrameData info)
{

}

// Called when the state of the playable is set to Paused
public override void OnBehaviourPause(Playable playable, FrameData info)
{

}

// Called each frame while the state is set to Play
public override void PrepareFrame(Playable playable, FrameData info)
{
//PrepareFrame方法每帧都会执行,可能当前还没执行到绑定exposedObjectValue对象
//所以exposedObjectValue会有空的情况
if(exposedObjectValue != null)
{
//获取PlayableDirector对象
PlayableDirector playableDirector = playable.GetGraph<Playable>().GetResolver() as PlayableDirector;

Debug.LogFormat("playableDirector : {0} exposedObjectValue : {1}", playableDirector.gameObject.name, exposedObjectValue.name);
}
}
}

下面是NewPlayableAsset中的代码,其中不仅要声明序列化内容,在PlayableBehaviour脚本中处理的结果也是要在Assets脚本中返回的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;

[System.Serializable]
public class NewPlayableAsset : PlayableAsset
{
//序列化对象
public ExposedReference<GameObject> exposedValue;
// Factory method that generates a playable based on this asset
public override Playable CreatePlayable(PlayableGraph graph, GameObject go)
{
//创建脚本
NewPlayableBehaviour behaviour = new NewPlayableBehaviour();
//向脚本中传递序列化对象
behaviour.exposedObjectValue = exposedValue.Resolve(graph.GetResolver());
return ScriptPlayable<NewPlayableBehaviour>.Create(graph, behaviour);
}
}

序列化对象

参考

Unity - Scripting API: ExposedReference (unity3d.com)

自定义Track

Timeline默认只支持GameObject、Animator和AudioSource,但是我们可以自定义拓展Track,这样就可以拖入自定义对象格式了。

新建MyTrack脚本,自定义Track需要继承TrackAsset,并且可以重写CreatePlayable()CreateTrackMixer()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEngine.Playables;

//设置Track的颜色
[TrackColor(1f,0f,0f)]
//设置Track的资源对象
[TrackClipType(typeof(NewPlayableAsset))]
//自定义绑定对象
[TrackBindingType(typeof(MyTrackType))]
public class MyTrack : TrackAsset
{
protected override Playable CreatePlayable(PlayableGraph graph, GameObject gameObject, TimelineClip clip)
{
Debug.Log("CreatePlayable创建");
return base.CreatePlayable(graph, gameObject, clip);
}

public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
{
Debug.Log("CreateTrackMixer创建");
return base.CreateTrackMixer(graph, go, inputCount);
}
}

新建MyTrackType脚本,用于在自定义Track中做绑定,将它挂载在一个GameObject上然后再绑在Track里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyTrackType : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{

}
}

自定义Track效果