注:软件版本Unity 6.0 + Timeline 1.8.7
作者:CSDN @ RingleaderWang
原文:《Unity第25期——Timeline结构及其源码浅析》
文章首发Github👍:《Timeline结构及其源码浅析》
Bilibili 视频版👍👍
:《Timeline结构及其源码解析》https://www.bilibili.com/video/BV1bHjYzNE35
PlayableGraph 的生命周期
原文运行时多次提到了 PrepareFrame、ProcessFrame,其实这就是ScriptPlayable在某段生命周期执行的回调函数,我们可以利用 ScriptPlayable 传入自定义PlayableBehaviour 来干涉Playable的生命周期。
Playable的生命周期有:
GraphStart
:graph play 开始时GraphStop
:graph stop时PlayableCreate
:Playable Create时PlayableDestroy
:Playable Destroy时BehaviourPlay
:Playable 运行时BehaviourPause
:Playable 暂停时PrepareFrame
:每帧处理数据前ProcessFrame
:每帧开始处理数据时
当然可能并非都是这些生命周期,比如AnimationScriptPlayable
就有个ProcessAnimation
和ProcessRootMotion
过程,可能就没有所谓的ProcessFrame了。
当然对于ScriptPlayable,上面的生命周期是确定的,在Playable处于上面的生命周期时,便会执行对应的回调函数(注册在PlayableBehaviour里)。
下面的自定义PlayableBehaviour就是简单打印回调方法和playable:
public class TimelineTestForAnimationScriptBehaviour : PlayableBehaviour{public override void OnGraphStart(Playable playable){DebugLog(playable,"OnGraphStart");}private static void DebugLog(Playable playable, string methodName){Debug.Log($"Playable by in/output cnt_{playable.GetInputCount()}{playable.GetOutputCount()} Behaviour:{methodName}");}public override void OnGraphStop(Playable playable){DebugLog(playable,"OnGraphStop");}public override void OnPlayableCreate(Playable playable){DebugLog(playable,"OnPlayableCreate");}public override void OnPlayableDestroy(Playable playable){DebugLog(playable,"OnPlayableDestroy");}public override void OnBehaviourPlay(Playable playable, FrameData info){DebugLog(playable,"OnBehaviourPlay");}public override void OnBehaviourPause(Playable playable, FrameData info){DebugLog(playable,"OnBehaviourPause");}public override void PrepareFrame(Playable playable, FrameData info){DebugLog(playable,"PrepareFrame");}public override void ProcessFrame(Playable playable, FrameData info, object playerData){DebugLog(playable,"ProcessFrame");}}
生命周期验证
上面生命周期的理解还比较模糊,尤其父子节点回调执行顺序是什么需要验证。
验证代码如下(给不同节点设置不同端口数能很方便辨析是哪个节点的回调):
using System;
using Scenes.TimelineTest.scripts;
using UnityEditor;
using UnityEngine;
using UnityEngine.Playables;public class TimelineTestForLifeCycle : MonoBehaviour
{public PlayableGraph graph;private ScriptPlayable<TimelineTestForAnimationScriptBehaviour> playable3;public void CreateGraph(){DestroyGraph();graph = PlayableGraph.Create("TimelineTestForLifeCycle");ScriptPlayableOutput output = ScriptPlayableOutput.Create(graph,"LifeCycleTestOutput");var playable5 = ScriptPlayable<TimelineTestForAnimationScriptBehaviour>.Create(graph,5);var playable4 = ScriptPlayable<TimelineTestForAnimationScriptBehaviour>.Create(graph,4);playable3 = ScriptPlayable<TimelineTestForAnimationScriptBehaviour>.Create(graph,3);var playable2 = ScriptPlayable<TimelineTestForAnimationScriptBehaviour>.Create(graph,2);var playable1 = ScriptPlayable<TimelineTestForAnimationScriptBehaviour>.Create(graph,1);output.SetSourcePlayable(playable5,0);output.SetWeight(1);playable5.ConnectInput(0,playable1,0,1);playable5.ConnectInput(1,playable2,0,0.5f);playable3.Pause();playable5.ConnectInput(2,playable3,0,1);playable5.ConnectInput(3,playable4,0,0);var playable9 = ScriptPlayable<TimelineTestForAnimationScriptBehaviour>.Create(graph,9);var playable8 = ScriptPlayable<TimelineTestForAnimationScriptBehaviour>.Create(graph,8);playable9.ConnectInput(0,playable8,0,1);}public void PlayPlayable3(){playable3.Play();}public void PausePlayable3(){playable3.Pause();}public void DestroyPlayable3(){playable3.Destroy();}public void PlayGraph(){graph.Play();}private void OnDestroy(){DestroyGraph();}public void DestroyGraph(){if (graph.IsValid()){Debug.Log("Execute Graph Destroy");graph.Destroy();Debug.Log("Graph destroyed");}}
}[CustomEditor(typeof(TimelineTestForLifeCycle))]
class TimelineTestForLifeCycleEditor : Editor
{public override void OnInspectorGUI(){base.OnInspectorGUI();TimelineTestForLifeCycle script = (TimelineTestForLifeCycle) target;if (GUILayout.Button("CreateGraph")){script.CreateGraph();}if (GUILayout.Button("PlayGraph")){script.PlayGraph();}if (GUILayout.Button("Destroy graph")){script.DestroyGraph();}if (GUILayout.Button("Play Playable3")){script.PlayPlayable3();}if (GUILayout.Button("Pause Playable3")){script.PausePlayable3();}if (GUILayout.Button("Destroy Playable3")){script.DestroyPlayable3();}}
}
点击 “CreateGraph” 后的 graph 结构(图中标注的是节点设置的play state和连接权重):
当前脚本主要测试graph的 create与play(除了prepareFrame、processFrame),验证:
- graph不play,playable节点是否会运行
- 没有playableOutput,playable节点是否会运行
- 输出权重为0,playable节点是否会运行
- pause状态,playable节点是否会运行
- PrepareFrame、ProcessFrame顺序
- Graph destroy执行效果
打印结果:
// 打印
Playable by in/output cnt_51 Behaviour:OnPlayableCreate
Playable by in/output cnt_41 Behaviour:OnPlayableCreate
Playable by in/output cnt_31 Behaviour:OnPlayableCreate
Playable by in/output cnt_21 Behaviour:OnPlayableCreate
Playable by in/output cnt_11 Behaviour:OnPlayableCreate
Playable by in/output cnt_91 Behaviour:OnPlayableCreate
Playable by in/output cnt_81 Behaviour:OnPlayableCreate
Playable by in/output cnt_51 Behaviour:OnGraphStart
Playable by in/output cnt_51 Behaviour:OnBehaviourPlay
Playable by in/output cnt_11 Behaviour:OnGraphStart
Playable by in/output cnt_11 Behaviour:OnBehaviourPlay
Playable by in/output cnt_21 Behaviour:OnGraphStart
Playable by in/output cnt_21 Behaviour:OnBehaviourPlay
Playable by in/output cnt_31 Behaviour:OnGraphStart
Playable by in/output cnt_31 Behaviour:OnBehaviourPause
Playable by in/output cnt_41 Behaviour:OnGraphStart
Playable by in/output cnt_41 Behaviour:OnBehaviourPlay Playable by in/output cnt_51 Behaviour:PrepareFrame
Playable by in/output cnt_11 Behaviour:PrepareFrame
Playable by in/output cnt_21 Behaviour:PrepareFrame
Playable by in/output cnt_41 Behaviour:PrepareFrame
Playable by in/output cnt_11 Behaviour:ProcessFrame
Playable by in/output cnt_21 Behaviour:ProcessFrame
Playable by in/output cnt_41 Behaviour:ProcessFrame
Playable by in/output cnt_51 Behaviour:ProcessFrame Graph destroyed
Playable by in/output cnt_51 Behaviour:OnBehaviourPause
Playable by in/output cnt_11 Behaviour:OnBehaviourPause
Playable by in/output cnt_21 Behaviour:OnBehaviourPause
Playable by in/output cnt_41 Behaviour:OnBehaviourPause
Playable by in/output cnt_51 Behaviour:OnGraphStop
Playable by in/output cnt_51 Behaviour:OnPlayableDestroy
Playable by in/output cnt_41 Behaviour:OnGraphStop
Playable by in/output cnt_41 Behaviour:OnPlayableDestroy
Playable by in/output cnt_31 Behaviour:OnGraphStop
Playable by in/output cnt_31 Behaviour:OnPlayableDestroy
Playable by in/output cnt_21 Behaviour:OnGraphStop
Playable by in/output cnt_21 Behaviour:OnPlayableDestroy
Playable by in/output cnt_11 Behaviour:OnGraphStop
Playable by in/output cnt_11 Behaviour:OnPlayableDestroy
Playable by in/output cnt_91 Behaviour:OnPlayableDestroy
Playable by in/output cnt_81 Behaviour:OnPlayableDestroy
结合Graph Monitor 可以得出结论:
- graph不play,playable节点不会运行
- 没有连ScriptPlayableOutput,ScriptPlayable节点不会运行(playable time不随时间增加),且除了OnPlayableCreate和OnPlayableDestroy,其他回调方法不会触发
- 输出权重为0,playable节点依然运行
- pause状态,playable节点不会运行
- graph未play,playable执行pause并不会触发OnBehaviourPause
- graph 执行Play() 后:若playable是playing状态时,则触发OnBehaviourPlay回调;是paused状态时,则触发OnBehaviourPause回调
- 会以前序遍历(父节点优先)的方式触发OnGraphStart 和 OnBehaviourPlay/Pause
- 会以前序遍历(父节点优先)的方式触发PrepareFrame回调
- 会以后序遍历(子节点优先)的方式触发ProcessFrame回调
- 没有连接playableOutput的playable节点不会触发OnGraphStart/OnGraphStop回调
- 直接Destroy graph时,如果graph未stop会先触发OnBehaviourPause、OnGraphStop回调,这两个回调的触发逻辑与先Stop Graph后Destroy Graph 略有不同,如下日志:
// 先Stop graph 再 Destroy graph的日志(与直接Destroy graph有差异)
graph.Stop()Playable by in/output cnt_51 Behaviour:OnBehaviourPause
Playable by in/output cnt_51 Behaviour:OnGraphStop
Playable by in/output cnt_11 Behaviour:OnBehaviourPause
Playable by in/output cnt_11 Behaviour:OnGraphStop
Playable by in/output cnt_21 Behaviour:OnBehaviourPause
Playable by in/output cnt_21 Behaviour:OnGraphStop
Playable by in/output cnt_31 Behaviour:OnBehaviourPause
Playable by in/output cnt_31 Behaviour:OnGraphStop
Playable by in/output cnt_41 Behaviour:OnBehaviourPause
Playable by in/output cnt_41 Behaviour:OnGraphStop Graph.Destroy() Playable by in/output cnt_51 Behaviour:OnPlayableDestroy
Playable by in/output cnt_41 Behaviour:OnPlayableDestroy
Playable by in/output cnt_31 Behaviour:OnPlayableDestroy
Playable by in/output cnt_21 Behaviour:OnPlayableDestroy
Playable by in/output cnt_11 Behaviour:OnPlayableDestroy
Playable by in/output cnt_91 Behaviour:OnPlayableDestroy
Playable by in/output cnt_81 Behaviour:OnPlayableDestroy
完整生命周期: