1. 程式人生 > >Timeline擴展功能實踐指南

Timeline擴展功能實踐指南

this 問題 屬性。 數據 WHID ooo 輸出 五個 amp

轉載於http://forum.china.unity3d.com/forum.php?mod=viewthread&tid=32842,介紹了timeline的軌道擴展

Timeline功能隨Unity 2017.1發布後,收到了大量相關反饋,我們發現很多開發者不希望只把Timeline用作簡單的序列工具。我們分享過文章《為Timeline創造性地設計腳本》,介紹如何利用Timeline的非常規用途。

本文將介紹如何使用Timeline來推動對話、支持分支功能以及連接遊戲的AI系統。

關於Timeline
什麽是Timeline?Timeline是一個線性編輯工具,用於序列化不同元素,包括動畫剪輯、音樂、音效、攝像機畫面、粒子特效以及其它Timeline。它大體上和Premiere?、After Effects?或Final Cut?類似,不同之處在於它為實時播放而設計。

Timeline一開始就以可擴展性為主要目標而設計,開發團隊在設計該功能時考慮到,除了內置剪輯和軌道外,用戶還會創建自制剪輯和軌道。所以用戶提出了很多有關在Timeline使用腳本的問題。

<ignore_js_op>技術分享圖片



Playable API
Playables API是一組強大的API,它能讓你讀取和混合多個數據源。例如:動畫、音頻等,並通過輸出內容進行播放。該系統提供精準的程序控制,它的性能開銷較低,並且針對性能進行了優化。

Timeline是基於Playables API實現的。當開始播放Timeline時,會構建一個由Playables節點組成的視圖。它們會以PlayableGraph樹形結構進行組織。

如果想要在場景中可視化PlayableGraph,例如:Animator和Timeline,你可以下載PlayableGraph Visualizer工具。本文將使用該工具可視化自定義剪輯的視圖。更多關於Playable API和Animator相關視圖的信息,請閱讀:《Timeline功能亮點Playable API》。

演示項目
下文將展示四個簡單示例,介紹如何使用Timeline的擴展功能。首先會介紹在Timeline添加腳本的最簡單方法。然後再逐步增加更多概念,充分使用Timeline的更多功能。

本文所有示例內容你請訪問下面地址下載:

本帖隱藏的內容

鏈接: https://pan.baidu.com/s/13Uu4NcsZMQXCf-GeI8kVOQ 密碼: skzg




請註意,項目中將使用前綴來區分每個示例中的各個類,例如“Simple_”、“Track_”和“Mixer_”等。而在後文代碼中,為提高可讀性則省略了這些前綴。

示例1:自定義剪輯
第一個示例非常簡單,目標是修改Light組件上的顏色和亮度屬性,該組件帶有自定義剪輯。

創建自定義剪輯需要二個腳本:
1、一個用於存放數據:從PlayableAsset繼承
2、另一個用於處理邏輯:從PlayableBehaviour繼承

Playable API的核心原則是分離邏輯和數據。所以首先需要創建PlayableBehaviour,使用該API編寫行為,代碼如下:

[C#] 純文本查看 復制代碼 ?
public class LightControlBehaviour : PlayableBehaviour { public Light light = null; public Color color = Color.white; public float intensity = 1f; public override void ProcessFrame(Playable playable, FrameData info, object playerData) { if (light != null) { light.color = color; light.intensity = intensity; } } }



該代碼實現了什麽功能呢?首先它確定需要修改Light組件的哪些屬性。而且PlayableBehaviour 擁有名為ProcessFrame 的方法可供重寫。

每次更新時都會調用ProcessFrame。在該方法中,你可以設置Light組件的屬性。然後為自定義剪輯創建PlayableAsset,代碼如下:

[C#] 純文本查看 復制代碼 ?
public class LightControlAsset : PlayableAsset { public ExposedReference<Light> light; public Color color = Color.white; public float intensity = 1.0f; public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) { var playable = ScriptPlayable<LightControlBehaviour>.Create(graph); var lightControlBehaviour = playable.GetBehaviour(); lightControlBehaviour.light = light.Resolve(graph.GetResolver()); lightControlBehaviour.color = color; lightControlBehaviour.intensity = intensity; return playable; } }



PlayableAsset有二個目的。首先它包含剪輯數據,因為它會在Timeline資源中序列化。其次它會構建PlayableBehaviour ,最後呈現為Playable視圖。

第一行代碼如下:

[C#] 純文本查看 復制代碼 ?
var playable = ScriptPlayable<LightControlBehaviour>.Create(graph);



該代碼會新建Playable,為它附加自定義行為LightControlBehaviour。然後可以在PlayableBehaviour設置光的屬性。

那麽代碼中ExposedReference的作用是什麽?由於PlayableAsset 是個資源,它不能直接引用場景中的對象。此時ExposedReference 會充當一個約定,表示在調用CreatePlayable 時,會解析一個對象。

現在可以在時間軸中添加Playable Track,並右鍵點擊該新軌道添加自定義剪輯。將Light組件指定給該剪輯並查看結果。

https://v.qq.com/x/page/p07816563al.html

在這種情況下,內置Playable Track是個通用軌道,它能接收簡單的Playable剪輯,例如:剛創建的剪輯。對於較復雜的情況,需要在專用軌道上托管剪輯。

示例2:自定義軌道
第一個示例的特點是每次添加自定義剪輯時,都要指定Light組件到每個剪輯上,如果剪輯數量較多,該過程非常麻煩。可以使用軌道的Bound(綁定)對象解決該問題。

<ignore_js_op>技術分享圖片
軌道結構圖



軌道上可以綁定對象或組件,這意味著該軌道上的每個剪輯可以直接在綁定對象上操作。這是個很常見的行為,同時也是Animation、Activation和Cinemachine軌道的工作方式。

如果想要修改多個剪輯上的Light屬性,可以創建自定義軌道,將Light組件作為綁定對象。創建自定義軌道需要擴展TrackAsset的腳本,代碼如下:

[C#] 純文本查看 復制代碼 ?
[TrackClipType(typeof(LightControlAsset))] [TrackBindingType(typeof(Light))] public class LightControlTrack : TrackAsset {}



這段代碼有二個屬性:
1、TrackClipType 指定該軌道將接受的PlayableAsset 類型。本例指定自定義LightControlAsset。
2、TrackBindingType 指定軌道要求綁定的類型,例如遊戲對象、組件或資源。本例為Light組件。

現在還需要對PlayableAsset和PlayableBehaviour 稍作修改,從而使它們適用於軌道上。下面註釋掉了不必要代碼行,以供參考。

[C#] 純文本查看 復制代碼 ?
public class LightControlBehaviour : PlayableBehaviour { //public Light light = null; public Color color = Color.white; public float intensity = 1f; public override void ProcessFrame(Playable playable, FrameData info, object playerData) { Light light = playerData as Light; if (light != null) { light.color = color; light.intensity = intensity; } } }



PlayableBehaviour 目前不需要Light變量。本示例中,ProcessFrame 方法直接提供軌道的綁定對象,只需將該對象轉換為合適類型即可。

[C#] 純文本查看 復制代碼 ?
public class LightControlAsset : PlayableAsset { //public ExposedReference<Light> light; public Color color = Color.white; public float intensity = 1f; public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) { var playable = ScriptPlayable<LightControlBehaviour>.Create(graph); var lightControlBehaviour = playable.GetBehaviour(); //lightControlBehaviour.light = light.Resolve(graph.GetResolver()); lightControlBehaviour.color = color; lightControlBehaviour.intensity = intensity; return playable; } } PlayableAsset 不需要為Light組件保留ExposedReference 。該引用將由軌道托管,並直接交給PlayableBehaviour。 我們可以在Timeline中添加LightControl軌道,並給它綁定Light組件。現在給該軌道添加的每個剪輯都將根據該Light組件進行操作。 [url]https://v.qq.com/x/page/t0781rsxun3.html[/url] 如果使用Graph Visualizer展示該視圖,效果如下: [align=center] [attach]15184[/attach][/align] 上圖中剪輯顯示為右邊名字相同的五個方塊。可以把每個方塊看作軌道。然後所有軌道都進入紫色方塊表示的Timeline中。 請註意,Playable粉色方塊實際上是Unity創建的Playable混合器。所以它和剪輯的顏色相同。混合器的概念將在示例3中說明。 [b]示例3:用混合器混合剪輯[/b] Timeline支持重疊剪輯,從而在剪輯之間創建混合效果或交叉漸變效果。自定義剪輯也支持混合效果。為了啟用混合功能,需要創建混合器,它能訪問所有剪輯數據,然後混合數據。 混合器由PlayableBehaviour派生,就像前面使用的LightControlBehaviour。實際上混合器還使用ProcessFrame 函數。關鍵區別在於,該Playable會通過重寫CreateTrackMixer函數在軌道腳本中聲明為混合器。 LightControlTrack腳本代碼如下: [mw_shl_code=csharp,false] [TrackClipType(typeof(LightControlAsset))] [TrackBindingType(typeof(Light))] public class LightControlTrack : TrackAsset { public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount) { return ScriptPlayable<LightControlMixerBehaviour>.Create(graph, inputCount); } }



創建該軌道的Playable Graph後,它還會創建一個新行為即混合器,並將該行為連接到軌道上的所有剪輯。

你還要將邏輯從PlayableBehaviour 上移動到該混合器中。因此PlayableBehaviour中的代碼較少。

[C#] 純文本查看 復制代碼 ?
public class LightControlBehaviour : PlayableBehaviour { public Color color = Color.white; public float intensity = 1f; }



該代碼只包含運行時中來自PlayableAsset 的數據。而混合器將擁有ProcessFrame 函數中的所有邏輯:

[C#] 純文本查看 復制代碼 ?
public class LightControlMixerBehaviour : PlayableBehaviour { // 註意:該函數會在運行時和編輯時調用。在設置屬性數值時要註意這一點。 public override void ProcessFrame(Playable playable, FrameData info, object playerData) { Light trackBinding = playerData as Light; float finalIntensity = 0f; Color finalColor = Color.black; if (!trackBinding) return; int inputCount = playable.GetInputCount (); //get the number of all clips on this track for (int i = 0; i < inputCount; i++) { float inputWeight = playable.GetInputWeight(i); ScriptPlayable<LightControlBehaviour> inputPlayable = (ScriptPlayable<LightControlBehaviour>)playable.GetInput(i); LightControlBehaviour input = inputPlayable.GetBehaviour(); finalIntensity += input.intensity * inputWeight; finalColor += input.color * inputWeight; } trackBinding.intensity = finalIntensity; trackBinding.color = finalColor; } }



混合器可以訪問軌道上的所有剪輯。本示例中,需要查看剪輯當前參與混合的所有亮度數值和顏色,所以要使用for循環在它們之中叠代。每次循環都要訪問輸入內容(GetInput(i)),並使用每個剪輯的權重(GetInputWeight(i))來構建最終數值,從而獲得該剪輯對混合內容的貢獻量。

假設要混合二個剪輯:一個剪輯貢獻紅色,另一個剪輯貢獻白色。當混合內容為四分之一時,顏色由0.25 * Color.red + 0.75 * Color.white計算而來,這樣會得到稍淡一點的紅色。

在循環結束時,要將整體結果應用到綁定的Light組件。得到如下內容:

https://v.qq.com/x/page/l0781srhmr7.html

<ignore_js_op>技術分享圖片



可以看出,紅色方塊表示你編寫的混合器Playable,你可以完全控制它。這和示例2相反,示例2的混合器是由Unity默認提供的。

因為該視圖處於混合中間,第二和第三個綠色方塊都有白線連接著混合器,表示它們各自權重約有0.5。

請註意,不管何時在混合器實現混合,邏輯內容都取決於開發者。混合二個顏色很簡單,但混合AI系統中二個代表不同AI狀態的剪輯會產生什麽結果呢?是否會在UI出現二行對話?如何在定格動畫中混合二個靜態姿勢呢?

或許混合不是連續的,但它是以“階梯式”進行的,所以姿勢會以離散增量相互變形,例如:0, 0.25, 0.5, 0.75, 1。有了這個功能強大的系統,便可以得到各種有趣的情景。

示例4:為自定義剪輯設置動畫
作為本指南最後一部分,我們首先來回顧一下前面的示例,然後使用“模板”來實現另一種移動數據的方式。該方法的優點是,它能讓你設置模板屬性的關鍵幀,從而可以在Timeline中為自定義剪輯創建動畫。

在前面示例中有一個Light組件的引用,指向PlayableAsset和PlayableBehaviour上的顏色和亮度。該數據在PlayableAsset 的檢視窗口上設置,然後在創建視圖時,會在運行時復制到PlayableBehaviour 中。

該方法能有效處理數據,但會復制之後需要一直保持同步的數據。但復制這些數據很容易產生錯誤。或者你也可以通過在PlayableAsset創建引用,從而使用PlayableBehaviour 的“模板”概念。

因此首先如下重寫LightControlAsset :

[C#] 純文本查看 復制代碼 ?
public class LightControlAsset : PlayableAsset { public LightControlBehaviour template; public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) { var playable = ScriptPlayable<LightControlBehaviour>.Create(graph, template); return playable; } }



LightControlAsset 現在只有對LightControlBehaviour的引用,而不是包含它的數據。代碼量比之前更少。

LightControlBehaviour 保持原樣即可:

[C#] 純文本查看 復制代碼 ?
[System.Serializable] public class LightControlBehaviour : PlayableBehaviour { public Color color = Color.white; public float intensity = 1f; }



對該模板的引用會在選中Timeline剪輯時,自動產生如下檢視窗口:

<ignore_js_op>技術分享圖片



完成該腳本後,就可以開始設置動畫了。

請註意,如果創建新剪輯,你將在軌道標題欄看到紅色的圓形按鈕。它表示該剪輯現在不需要添加Animator就可以設置關鍵幀。你只需點擊該紅色按鈕,選取剪輯,定位創建該關鍵幀的播放頭位置,然後修改該屬性數值即可。

你也可以點擊白色的方形按鈕,展開Curves視圖來查看關鍵幀創建的曲線。

<ignore_js_op>技術分享圖片



這樣做還有的優點是你可以雙擊Timeline剪輯,讓Unity打開Animation面板,然後將該剪輯鏈接到Timeline。你可以註意到,當下圖的按鈕出現時,這些剪輯將被鏈接。

<ignore_js_op>技術分享圖片



這樣在對Timeline和Animation窗口進行處理時,播放頭將保持同步,從而完全控制關鍵幀。你可以在Animation窗口修改動畫,在更便捷的環境中處理關鍵幀。

<ignore_js_op>技術分享圖片

07.png (15.52 KB, 下載次數: 2)

下載附件

2018-9-10 23:49 上傳



在該視圖中,你可以充分使用動畫曲線和攝影表(Dopesheet),來優化自定義剪輯的動畫。

請註意,在通過這種方法設置動畫時,會創建動畫剪輯。可以在Timeline資源下找到這些剪輯。

<ignore_js_op>技術分享圖片



結語
我們希望本文能作為實用向導指南,介紹通過Timeline可以實現的諸多功能,從而讓你使用腳本將Timeline提升到新的水平。我們也希望大家使用Timeline創作出更多優秀的作品!

更多Unity最新最全面的功能介紹盡在Unity官方中文論壇(UnityChina.cn)!

Timeline擴展功能實踐指南