Timeline 理解与API解读
Timeline自有组件:
Activation Track
Animation Track
Audio Track
Control Track
Playable Track
Track Group
PlayableGraph提供了Timeline的可视化图形结构,暂不支持编辑功能。如图:
Playable Track使Timeline具备可扩展性。
PlayableAsset: 一个针对资源的基类,可以用于运行时实例化一个Playable脚本所驱动。
PlayableBehaviour:可以向PlayableGraph增加用户的行为。
PlayableBehaviour必须是连接到激活的输出的PlayableGraph的分支的一部分。
关于该Playable的公有方法(都是回调函数):
OnBehaviourPuase : 当Playable的play state变成PlayState.Paused的时候,这个方法被调用;
OnBehaviourPlay : 当Playable的PlayState变成PlayState.Playing的时候,这个方法被调用;
OnGraphStart: 这个方法别调用,当前拥有PlayableBehavior的GraphStart开始的时候;
OnGraphStop: 这个方法别调用,当前拥有PlayableBehavior的GraphStop开始的时候; OnGraphCreate: 这个方法别调用,当前拥有PlayableBehavior的GraphCreate开始的时候; OnGraphDestory: 这个方法别调用,当前拥有PlayableBehavior的GraphDestory开始的时候
PrepareFrame: 这个方法在Playable的PrepareFrame阶段被调用;
ProcessFrame: 这个方法在Playable的ProcessFrame阶段被调用;
1 | using UnityEngine; |
``` C#
using UnityEngine;
using UnityEngine.Playables;
///
// A behaviour that is attached to a playable
public class LightControlBehaviour : PlayableBehaviour
{
public Light light = null;
public Color color = Color.white;
public float intensity = 1;
// Called when the owning graph starts playing
public override void OnGraphStart(Playable playable)
{
Debug.Log("OnGraphStart");
}
// Called when the owning graph stops playing
public override void OnGraphStop(Playable playable)
{
Debug.Log("OnGraphStop");
}
// Called when the state of the playable is set to Play
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
Debug.Log("OnBehaviourPlay");
}
// Called when the state of the playable is set to Paused
public override void OnBehaviourPause(Playable playable, FrameData info)
{
Debug.Log("OnBehaviourPause");
}
// Called each frame while the state is set to Play
public override void PrepareFrame(Playable playable, FrameData info)
{
Debug.Log("PrepareFrame");
}
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
if (light != null)
{
light.color = color;
light.intensity = intensity;
}
Debug.Log("ProcessFrame");
}
}
``` C#
Unity Timeline封装,方便动态设置Timeline控制对象和参数
TimelineUnit.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEngine.Playables;
public class TimelineUnit
{
public string name;
public PlayableDirector director;
public PlayableAsset asset;
public Dictionary
public Dictionary
public void Init(string name, PlayableDirector director, PlayableAsset asset)
{
director.playableAsset = asset;
this.name = name;
this.director = director;
this.asset = asset;
bindings = new Dictionary<string, PlayableBinding>();
clips = new Dictionary<string, Dictionary<string, PlayableAsset>>();
foreach (var o in asset.outputs)
{
var trackName = o.streamName;
bindings.Add(trackName, o);
var track = o.sourceObject as TrackAsset;
var clipList = track.GetClips();
foreach (var c in clipList)
{
if (!clips.ContainsKey(trackName))
{
clips[trackName] = new Dictionary<string, PlayableAsset>();
}
var name2Clips = clips[trackName];
if (!name2Clips.ContainsKey(c.displayName))
{
name2Clips.Add(c.displayName, c.asset as PlayableAsset);
}
}
}
}
public void SetBinding(string trackName, Object o)
{
director.SetGenericBinding(bindings[trackName].sourceObject, o);
}
public T GetTrack<T>(string trackName) where T : TrackAsset
{
return bindings[trackName].sourceObject as T;
}
public T GetClip<T>(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;
}
public void Play()
{
director.Play();
}
}
TimelineHelper.cs
using UnityEngine;
using UnityEngine.Playables;
public class TimelineHelper
{
public static TimelineUnit AddTimeline(GameObject go, string timelineName)
{
var unit = new TimelineUnit();
var director = go.GetComponent
if (null == director)
director = go.AddComponent
var asset = Resources.Load
unit.Init(timelineName, director, asset);
return unit;
}
}
现在做一个Timeline,然后进行测试
一个AnimationTrack,一个PlayableTrack,动画轨道不多讲,难点是PlayableTrack,这个是可以自定义逻辑的轨道
需要写两个类,一个继承PlayableAsset,一个继承PlayableBehaviour
using UnityEngine;
using UnityEngine.Playables;
public class MoveObjPlayableAsset : PlayableAsset
{
public GameObject go;
public Vector3 pos;
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
var bhv = new MoveObjPlayableBehaviour();
bhv.go = go;
bhv.pos = pos;
return ScriptPlayable<MoveObjPlayableBehaviour>.Create(graph, bhv);
}
}
using UnityEngine;
using UnityEngine.Playables;
public class MoveObjPlayableBehaviour : PlayableBehaviour
{
public GameObject go;
public Vector3 pos;
public override void OnGraphStart(Playable playable)
{
base.OnGraphStart(playable);
Debug.Log("OnGraphStart=======================");
}
public override void OnGraphStop(Playable playable)
{
base.OnGraphStop(playable);
Debug.Log("OnGraphStop=======================");
}
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
base.OnBehaviourPlay(playable, info);
Debug.Log("OnBehaviourPlay=======================");
if (null != go)
{
go.transform.position = pos;
}
}
public override void OnBehaviourPause(Playable playable, FrameData info)
{
base.OnBehaviourPause(playable, info);
Debug.Log("OnBehaviourPause=======================");
if (null != go)
{
go.transform.position = Vector3.zero;
}
}
public override void OnBehaviourDelay(Playable playable, FrameData info)
{
base.OnBehaviourDelay(playable, info);
Debug.Log("OnBehaviourDelay=======================");
}
}
这样,就可以将MoveObjPlayableAsset脚本直接拖动到PlayableTrack轨道上,具体的脚本逻辑在MoveObjPlayableBehaviour中写,我这里的逻辑是改变物体的坐标,具体逻辑就看具体需求了,这里只是举个例子
程序入口我放在一个runner的脚本里,挂在场景中的一个物体上
using UnityEngine;
public class Runner : MonoBehaviour
{
void Start()
{
var go = new GameObject(“TimelineGo”);
var unit = TimelineHelper.AddTimeline(go, “myTimeline”);
unit.director.extrapolationMode = UnityEngine.Playables.DirectorWrapMode.Loop;
unit.SetBinding(“at”, go);
var p = unit.GetClip<MoveObjPlayableAsset>("myPlayable", "b");
p.go = gameObject;
unit.Play();
}
}
运行就可以看到有一个TimelineGo物体挂了Timeline运行了