第一次看到timeline觉得这个东西和以前自己的做视频时使用的剪辑软件video studio/premiere/After Effect等非常的相似,同样的视频轨道,随意滑动,非常便于操作 如下图所示。
现在开始简要介绍一下timeline的基本操作:
1.直接在面板上新建一个空物体,create empty,随便取个名字叫做timeline manger;
2.直接点击windows ,点击timeline editor;就会弹出如上图所所示的timeline 操作界面,然后删掉出现的第一个轨道。 因为没什么用。
3.点击timeline下面的add,就可以添加几个轨道: 其中
activeation track可以让物体在什么时间在什么地点出现或者消失;
animation track可以添加动画片段,或者自己点击红色圆圈自己录制动画;
audio track 可以添加音效片段;
playable track 可以写上控制代码,这里我在后面将重点讲解;
control track 自己还没有用过。。。。。;
目前网上已经有人将timeline的相关函数都进行了封装,我们可以很方便的进行使用。具体的封装方法见链接。点击打开链接
我自己稍微改了一下,具体代码见下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
public class TimelineUnit {
public string name;
public PlayableDirector director;
public PlayableAsset asset;
public Dictionary bindings;
public Dictionary> clips;
public Dictionary playables;
public void Init(string path, PlayableDirector director)
{
this.name = path; // director标签
this.director = director; // 获取当前组件
playables = new Dictionary();
InitPlayables(path); // 获得所有的动画
}
public void Switch(string assetName)
{
if (!playables.TryGetValue(assetName, out this.asset))
{
Debug.LogError("No Asset exist!");
return;
}
director.playableAsset = asset; // 导演Manager
bindings = new Dictionary(); // PlayableAsset下的所有binding
clips = new Dictionary>(); // binding里的所有clip
foreach (var o in asset.outputs) // 每一个binding,其实就是trackasset和需要动画的模型之间的链接关系
{
var trackname = o.streamName;
bindings.Add(trackname, o); // 每一个binding的名字和binding绑定
var track = o.sourceObject as TrackAsset; // 每个binding下的对象为track
var clipList = track.GetClips(); // 获得每一个track下的所有动画片段
foreach (var c in clipList) // 存入clips
{
if (!clips.ContainsKey(trackname))
{
clips[trackname] = new Dictionary();
}
var name2clips = clips[trackname];
if (!name2clips.ContainsKey(c.displayName))
{
name2clips.Add(c.displayName, c.asset as PlayableAsset);
}
}
}
}
// 清空数据
public void Remove()
{
bindings.Clear();
clips.Clear();
}
// 动画和模型进行绑定
public void SetBinding(string trackName, Object o)
{
director.SetGenericBinding(bindings[trackName].sourceObject, o);
}
// 获得动画轨道
public T GetTrack(string trackName) where T : TrackAsset
{
return bindings[trackName].sourceObject as T;
}
// 获得动画片段
public T GetClip(string trackName, string clipName) where T : PlayableAsset
{
if (clips.ContainsKey(trackName))
{
var track = clips[trackName];
if (track.ContainsKey(clipName))
{
return track[clipName] as T;
}
else
{
Debug.LogError("GetClip Error, Track does not contain clip, trackName: " + trackName + ", clipName: " + clipName);
}
}
else
{
Debug.LogError("GetClip Error, Track does not contain clip, trackName: " + trackName + ", clipName: " + clipName);
}
return null;
}
// 读入所有动画资源
private void InitPlayables(string path)
{
PlayableAsset[] o = Resources.LoadAll(path + "/");
foreach (var asset in o)
{
playables.Add(asset.ToString().Replace(" (UnityEngine.Timeline.TimelineAsset)", ""), asset);
Debug.Log(asset.ToString());
}
}
// 控制动画的播放,停止等
public void Play()
{
director.Play();
}
public void pause()
{
director.Pause();
}
public void Stop()
{
director.Stop();
}
public void Resume()
{
director.Resume();
}
}
下面是自己的代码:
(注意:TextPlayableBehaviour 不用挂在物体上,里面包含了不同情况下自动执行的逻辑)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.UI;
// A behaviour that is attached to a playable
public class TextPlayableBehaviour : PlayableBehaviour
{
public Text _text;
public string s;
private PlayableDirector director;
// Called when the owning graph starts playing
public override void OnGraphStart(Playable playable)
{
director = GameObject.Find("TimelineManager").GetComponent();//找到timelinemanger,取得控制权
}
Called each frame while the state is set to Play
public override void PrepareFrame(Playable playable, FrameData info)
{
_text.text = s;
}
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
_text.enabled = true;//为了让text可视化
// Debug.Log("为true了");
}
Called when the state of the playable is set to Paused
public override void OnBehaviourPause(Playable playable, FrameData info)
{
_text.enabled = false;
if (director.time > 0.4f)//因为未知原因导致,一开始就检测到了onbehavior pause,所以设定一定的时间范围,延缓0.4s再检测,此时onbehavior pause是在正常的地方停下
{
director.Pause();
}
}
Called when the owning graph stops playing
//public override void OnGraphStop(Playable playable) {
// //director.Pause();
// Debug.Log("OnGraphStop起作用了");
//}
Called when the state of the playable is set to Play
//public override void OnBehaviourPlay(Playable playable, FrameData info) {
// _text.enabled = true;
//}
Called when the state of the playable is set to Paused
//public override void OnBehaviourPause(Playable playable, FrameData info) {
// // director.Pause();
// Debug.Log("OnBehaviourPause起作用了");
//}
}
(注意:TextPlayableAsset写好后,直接在playableTrack中的直接add 就行了,这里主要是生成了一个具体的TextPlayableAsset,其中new了一个TextPlayableBehaviour 的对象)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.UI;
using TMPro;
[System.Serializable]
public class TextPlayableAsset : PlayableAsset
{
public ExposedReference text;
public string s;
// Factory method that generates a playable based on this asset
public override Playable CreatePlayable(PlayableGraph graph, GameObject go) {
TextPlayableBehaviour t = new TextPlayableBehaviour();
t._text = text.Resolve(graph.GetResolver());
t.s = s;
// Debug.Log("textplayableasset"+s);
// t._text.enabled = false;
return ScriptPlayable.Create(graph, t);
}
}
接下来就创建一个空物体,名字叫做timelinemanger,自己写一个timelinemanger的函数,里面new一个timelineunit的对象,就可以使用timeline中的各种方法了。(由于项目中的方法太复杂,也就不便于贴出来了)但是出于对读者的尊重,我还是写一个(awake的函数)吧。剩下的就是如何触发play 或者是stop了,这就交由读者了。
private void Awake () {
director = GetComponent();
text = GameObject.Find("text_result");
//如果没有PlayableDirector,则添加
if (null == director)
{
director = gameObject.AddComponent();
}
//初始化工作
helper = new TimelineUnit();
helper.Init("operaiton_Animation", director);
text.GetComponent().text = null;
helper.Switch(timeline_name);
helper.SetBinding("Animation Track", GameObject.Find("garbage car"));
}
其中在使用过成中的一些注意如下:
(1)setbinding函数必须得有一个动画轨道,也就是你必须得在使用switch()函数之后才能够调用setbinding函数将自己的动画与对象进行绑定。
(2)你可以参照下面的代码,直接复制粘贴一个PlayableAsset和PlayableBehaviour,这样你就可以动态的显示文字了,但是好像不能当时就改变(这里大家如果做到了,麻烦跟我说一下)。而且如果是多个PlayableAsset对应这同一个textUI,那么在playabletrack中的PlayableAsset最好起不一样的名字,因为会产生错误(比如找不到对应的PlayableAsset)。
(3)我不知道PlayableAsset中的ExposedReference是什么意思,为什么没有它就不能传值了,此处我并没有做过具体实验,(大牛如果知道,告诉小弟我一声)。