TimeLine开场动画
创建TimeLine
在菜单栏点击Window->Sequnence->Timeline
选中目标的GO,然后在Timeline
窗口新建一个Timeline
即可对选中的GO创建Timeline
组件
将动画Canvas拖入Timeline
栏并设置为Animation Track
,使用Timeline
窗口的录制功能即可实现动画录制
一个技巧:先在靠后位置完成动画的结尾,保存当前场景所有内容的位置,颜色,打开状态、大小等,然后从前往后以此制作动画过程。
为了保证动画在规定时间执行,因此可以将存放动画的Panel
放入Timeline
轴中,然后为期设置在动画节点才能启动
创建Timeline对话
在出场动画结束后,执行开启强制执行的对话,因此必须将该部分也放在Timeline
中,为了控制该部分内容,需要通过代码来控制Timeline
。
由于Timeline
本身没有对话相关的轨道和片段,因此需要编写相应的脚本来创建对应的动画片段。相应的就需要设计轨道脚本DialogueTrack
用于让Unity得知有自定义的轨道;轨道中的动画片段脚本DialogueClip
,用于能够在轨道中创建动画片段,以及最主要的动画片段内容DialogueBehaviour
,用于具体设计该动画需要呈现的内容。
新建代码脚本DialogueBehaviour.cs
,注意该类不是MonoBehaviour
,而是PlayableBehaviour
,
该脚本能够控制我们整个对话播放过程的执行,暂停等
1
2
3
4
5
6
7
8
9
10
11
|
public class DialogueBehaviour : PlayableBehaviour
{
private PlayableDirector _director;
public DialoguePiece dialoguePiece;
public override void OnPlayableCreate(Playable playable)
{
// 通过当前的播放的graph方向得到PlayableDirector
_director = (playable.GetGraph().GetResolver() as PlayableDirector);
}
}
|
然后创建一个用于控制我们的每一条轨道的内容的代码DialogueClip
,其需要继承两个特殊的接口PlayableAsset, ITimelineClipAsset
其中PlayableAsset
用于获得实例化的Playable
的资源,也就是每一条轨道上的内容
ITimelineClipAsset
是播放剪辑功能必备的组件
1
2
3
4
5
6
7
8
9
10
11
12
|
public class DialogueClip : PlayableAsset, ITimelineClipAsset
{
public DialogueBehaviour dialogueTemplate = new DialogueBehaviour();
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
/* 每次新建,都以dialogueTemplate为模板在graph中新建可以编辑的片段 */
var playable = ScriptPlayable<DialogueBehaviour>.Create(graph, dialogueTemplate);
return playable;
}
public ClipCaps clipCaps => ClipCaps.None;
}
|
创建Timeline
的Dialogue
轨道,在该代码中,只需要让系统知道,你在该轨道中放的片段类型是什么,不需要实际的代码
1
2
3
|
[TrackClipType(typeof(DialogueClip))]
public class DialogueTrack : TrackAsset
{}
|
从而得到以下内容
然后为了显示具体的对话内容,需要对DialogueBehaviour
中的部分方法进行重写,来执行该动画片段的具体内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class DialogueBehaviour : PlayableBehaviour
{
//....
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
// 呼叫启动对话UI
EventHandler.CallShowDialogueEvent(dialoguePiece);
if (Application.isPlaying)
{
if (dialoguePiece.hasToPause)
{
// 暂停Timeline,等待按下空格
TimelineManager.Instance.PauseTimeline(_director);
}
else
{
// 关闭当前dialogue
EventHandler.CallShowDialogueEvent(null);
}
}
}
}
|
由于设计对话的时候使用了 通过空格键在继续对话内容,因此为了实现对话持续需要继续增加按键播放。
而等待空格键的过程中,Timeline
必须停止,而且不能使用Timeline
自带的暂停功能,其会导致暂停结束后快速播放后续内容,因此需要通过一个Timeline
的控制器来控制其暂停。
而控制暂停的方式就是让TimeLine
的时间播放速度为0,当检测到空格则恢复播放
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
|
public class TimelineManager : Singleton<TimelineManager>
{
public PlayableDirector startDirector; /* 游戏开局的timeline */
private PlayableDirector _currentDirector; /* 当前正在播放的director */
private bool _isPause;
protected override void Awake()
{
base.Awake();
_currentDirector = startDirector;
}
private void Update()
{
if (_isPause && Input.GetKeyDown(KeyCode.Space))
{
_currentDirector.playableGraph.GetRootPlayable(0).SetSpeed(1d);
}
}
public void PauseTimeline(PlayableDirector director)
{
_currentDirector = director;
/* 获得当前director中graph中的根节点, 并将其速度设置为0 */
_currentDirector.playableGraph.GetRootPlayable(0).SetSpeed(0d);
_isPause = true;
}
}
|
控制Timeline启动与暂停
由于对话动画的播放并不是及时播放完成,因此上节代码按空格直接开启timeline的播放有误,本节将通过从Timeline
自带的方法中获得对话是否播放完成的标志_isDone
,通过该标志保证只有当对话动画播放完成才能启动timeline
设置标志
1
2
3
4
5
6
|
// timelineManager.cs
private bool _isDone;
public bool IsDone /* 为_isDone赋值 */
{
set => _isDone = value;
}
|
timeline播放时逐帧执行dialogpiece播放完成的赋值
1
2
3
4
5
6
7
8
9
|
// DialogueBehaviou.cs
/* 在timeline播放过程中逐帧执行 */
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
if (Application.isPlaying)
{
TimelineManager.Instance.IsDone = dialoguePiece.isDone;
}
}
|
保证对话框的关闭
由于OnBehaviourPlay
方法只能在timeline执行的时候才能启动,因此如果timeline结束前有对话框,那么关闭对话框的代码就不能执行,因此需要在timeline结束以后也执行关闭对话框
1
2
3
4
5
6
|
// DialogueBehaviou.cs
/* 如果timeline最后是对话框,则强制关闭对话框 */
public override void OnBehaviourPause(Playable playable, FrameData info)
{
EventHandler.CallShowDialogueEvent(null);
}
|
保证timeline开启的时候,游戏时间不会流逝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// DialogueBehaviou.cs
/// <summary>
/// 当当前对话开启的时候,游戏时间暂停
/// </summary>
/// <param name="playable"></param>
public override void OnGraphStart(Playable playable)
{
EventHandler.CallUpdateGameStateEvent(GameState.Pause);
}
public override void OnGraphStop(Playable playable)
{
EventHandler.CallUpdateGameStateEvent(GameState.Gameplay);
}
|
修改时间流逝的内容
1
2
3
4
5
|
// timeManager.cs
private void OnUpdateGameStateEvent(GameState state)
{
_gameClockPause = state == GameState.Gameplay;
}
|
获取当前场景内容
1
2
3
4
5
6
7
|
// TimelineManager.cs
private void OnAfterSceneLoadEvent()
{
_currentDirector = FindObjectOfType<PlayableDirector>();
if (_currentDirector != null)
_currentDirector.Play();
}
|