unity 官网查看。
个人博客查看。

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
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 UnityEngine;
using UnityEngine.Playables;

[System.Serializable]
public class LightControlAsset : PlayableAsset
{
/// <summary>
/// 由于PlayableAsset是个资源,它不能直接引用场景中的对象。此时ExposedReference会充当一个约定,表示在调用CreatePlayable时会解析一个对象。
/// </summary>
public ExposedReference<Light> light;
public Color color = Color.gray;
public float intensity = 1f;

// Factory method that generates a playable based on this asset
public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
Debug.Log("CreatePlayable");
var playable = ScriptPlayable<LightControlBehaviour>.Create(graph);
var lightControlBehaviour = playable.GetBehaviour();
lightControlBehaviour.light = light.Resolve(graph.GetResolver());
lightControlBehaviour.color = color;
lightControlBehaviour.intensity = intensity;
return playable;
}
}

``` C#
using UnityEngine;
using UnityEngine.Playables;

///

/// 目的:确定需要修改Light组件的哪些属性。而且PlayableBehaviour拥有名为ProcessFrame的方法可供重写。 ///
// 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 Dictionarybindings;
public Dictionary> clips;

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(“TimelineRes/“ + timelineName);
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运行了