技能编辑器前期准备
TimeLine的作用非常广泛,本人之前做剧情编辑器用timeline实现了剧情动画,现在想将timeline的概念融入技能系统当中,所以有了这篇文章。
说清楚什么是Timeline
以下是本人理解
首先我们把概念区间暂时限定在游戏世界中
timeline就是游戏运行过程总时间区间内指定实体们的某一段时间的游戏逻辑。
而好的timeline实现代码可以让我们方便的编辑一段timeline的逻辑和指定这段timeline的开始时间
为什么选用Slate而不是Unity自带的Timeline
先说结论: 为了能更好的编辑timeline逻辑。
本人想将timeline概念引入技能模块已经很久了,期间参考了各种资料以及各路大佬的意见最终得出了这个结论。
原因有2:
1.unity自带timeline是天然黑盒状态,当然你也可以让它变成白盒,但终究不是那么方便。
2.unity自带timeline实现框架的逻辑相对来说很乱且框架并不是相对独立的(听来的0.0本人没看过Unity Timeline底层源码),不易于扩展和维护。
解析Slate基本框架
IDirector--“导演接口”
用来主管Timeline的整体运行流程,可以说是整段timeline的最上层播放逻辑

IDirectable--“被导演指挥的演员们的接口”
IDirectable的继承者有三大类:Group,track,clip
这三大类在timeline驱动下生命周期相对独立,不存在生命周期函数的嵌套调用,但是有逻辑上的从属关系。
Group
track的集合

track
clip的集合

Clip
track中的一小段逻辑

Slate驱动方式设计
1.Slate自带的提供两种更新方式
分别是Unity自带的生命周期LateUpdate 和 FixedUpdate
会分别传入 deltaTime unscaledDeltaTime 或者 fixedDeltaTime
传入的时间将是我们完成驱动的重要数据
同样也是我们让slate适配技能模块的需要重点关注的地方
protected void LateUpdate() {
if ( isActive && ( updateMode == UpdateMode.Normal || updateMode == UpdateMode.UnscaledTime ) ) {
if ( isPaused ) {
Sample();
return;
}
var dt = updateMode == UpdateMode.Normal ? Time.deltaTime : Time.unscaledDeltaTime;
UpdateCutscene(dt);
}
}
//UNITY CALLBACK
protected void FixedUpdate() {
if ( isActive && updateMode == UpdateMode.AnimatePhysics ) {
if ( isPaused ) {
Sample();
return;
}
UpdateCutscene(Time.fixedDeltaTime);
}
}
2.采样函数
采样函数采的是我们编辑好的时间点
其中 Internal_SamplePointers 是主要驱动函数
InitializeTimePointers 用来初始化时间点
public void Sample(float time) {
currentTime = time;
//ignore same minmax times
if ( ( currentTime == 0 || currentTime == length ) && previousTime == currentTime ) {
return;
}
//Initialize time pointers if required.
if ( !preInitialized && currentTime > 0 && previousTime == 0 ) {
InitializeTimePointers();
}
//Sample started
if ( currentTime > 0 && currentTime < length && ( previousTime == 0 || previousTime == length ) ) {
if ( !Application.isPlaying || isActive ) {
OnSampleStarted();
}
}
//Sample pointers
if ( timePointers != null ) {
Internal_SamplePointers(currentTime, previousTime);
}
//Sample ended
if ( ( currentTime == 0 || currentTime == length ) && previousTime > 0 && previousTime < length ) {
if ( !Application.isPlaying || isActive ) {
OnSampleEnded();
}
}
previousTime = currentTime;
}
3.被采样的可视化
箭头所指就是slate采样函数中会采取的时间节点
通过时间点(TimePointers)slate可以判定出group,track,clip,是否处于生命周期中,并调用它们各自的生命周期函数,实现timeline效果。

4.Slate默认更新顺序
(Group Enter -> Track Enter -> Clip Enter | Clip Exit -> Track Exit -> Group Exit)
小结
以上就是slate的基本实现思路
slate已经在此思路框架之上为我们做好了:
CutScene--继承 IDirector 的导演类用来播放timeline,我们可以以此为父类或者 IDirector 直接继承继续扩展自己的播放类
CutsceneGroup--继承 IDirectable 的group基类,我们可以继承 CutsceneGroup 实现自己想要的 Group
CutsceneTrack--- 继承 IDirectable 的 Track- 基类 我们可以继承 CutsceneTrack 实现自己想要的 Track
ActionClip---- 继承 IDirectable 的 Clip 基类 我们可以继承 CutsceneGroup 实现自己想要的 Clip
同时Slate也帮我们完成了比较困难的动画轨道制作
等等...

Comments | NOTHING