寫在前面
- 在前幾次探索中,時不時都會看到Playable的身影,比如在Timeline面板上看到的Playable Track,在寫字幕軌腳本時看到的方法參數Playable Graph,我翻了翻關於Playable的相關博文,只是瀏覽程度的話確實沒太理解博文內容。
- 這次探索會圍繞這篇官方介紹博文“Playable API:定制你的動畫系統”展開,記下我在讀的過程中哪些地方沒讀懂,以及后來看了演示工程,自己另外寫了測試,來補足理解的過程。
探索
- 官方介紹博文“Playable API:定制你的動畫系統”:總的來說,Playable是一個系統。它支持動畫,而且它是除了legacy動畫、Mecanim動畫外的第三種(更好的)選擇。它以后還會支持音頻、視頻等更多功能。它的優點是,可在編輯模式下看大部分效果,不用狀態機,控制得更細。
沒讀懂的地方
- 官方博文沒有具體舉例實現效果,直接放理論和代碼,它講的若干Playable好處我都不知道如何體現。以下是我覺得很重要但沒讀懂的地方:
- 【問題1】Playable Graph 怎么用?
- 【問題2】Animator怎樣控制權重,Playable怎樣控制權重?
- 【問題3】Clip怎樣和Animator混用?
補足理解
環境
- 官方博文為了演示Playable Graph使用了Graph Visualizer工具,下載下來,把它安上。
- 官方博文中提到了示例工程Simple Animation,下載下來,打開工程看看里面有什么。
- Unity2019.2.6:因為Graph Visualizer工具和示例工程Simple Animation必須在Unity2018及以上版本才可運行,所以我的測試工程都會在Unity2019.2.6版本上運行。(我試過把它們安到Unity2017版本上,但沒有成功)
Graph Visualizer工具
- 顧名思義,這是個可視化Playable Graph的工具。下好該工具壓縮包並解壓后,如下圖,點擊Packages面板的加號按鈕導入它的package.json文件,即可安裝。安裝后在菜單欄Windows -> Analysis -> Playable Visualizer打開。
示例工程Simple Animation
- 在示例工程中,有3個場景,沒有說明文檔。隨便打開一個場景運行,看到了三個差不多的效果,再翻了翻它的代碼,竟然一時不知道它要告訴我什么。
頂上三個按鈕播放效果都是一樣的
- 示例看不懂沒關系,先給自己設下第一個測試小目標——怎樣用Playble播放動畫。畢竟要了解Playble,總得明白最簡單的怎么播動畫吧。再再具體一點,我設定了以下測試:
【測試1】旋轉Cube
- 首先,我找到了一篇個人博文,它有着最基本的用Playble播放動畫代碼。我稍微修改了一下腳本(改了Graph起名,刪注釋),如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Animations; using UnityEngine.Playables; [RequireComponent(typeof(Animator))] public class Playable_RotateCube : MonoBehaviour { public AnimationClip clip; private PlayableGraph graph; void Start() { graph = PlayableGraph.Create("RotateCube"); AnimationPlayableOutput animOutput = AnimationPlayableOutput.Create(graph, "Animation", GetComponent<Animator>()); AnimationClipPlayable clipPlay = AnimationClipPlayable.Create(graph, clip); animOutput.SetSourcePlayable(clipPlay); graph.Play(); // 另一種寫法,更方便的接口 //AnimationPlayableUtilities.PlayClip(GetComponent<Animator>(), clip, out graph); } void OnDisable() { graph.Destroy(); } }
- 我在場景中准備了Cube,和從Simple Animation示例工程拷過來的旋轉動畫RotateLegacy。Cube掛了上面的播動畫腳本,Animator組件去掉用不着的Animator Controller,具體設置如下:
結果運行時報錯“ArgumentException: Legacy clips cannot be used in Playables.”,官方解釋腳本放的Clip不能是Legacy類型。怎么知道一個Clip是不是Legacy類型呢?將該Clip的Inspector面板切換到Debug模式,看Legacy選項是否打鈎即可,如下圖。
於是,我將Clip改為RotateAnimator,可以成功運行。
- 運行時,Cube成功播放旋轉動畫
- 此時在Graph Visualizer窗口中,它的Graph如下圖。如腳本中寫的那樣,Graph中只有一個AnimationOuput(它的全稱是AnimationPlayableOutput),一個AnimationClip(它的全稱是AnimationClipPlayable)。到這里,能對Playable Graph有一個初印象,它就像它的名字一樣,相當於一張圖,圖上有着我們設置的各種Playable節點。
【測試2】cube一邊旋轉一邊移動
- 現在我不僅僅想要旋轉Cube,還想要它一邊旋轉一邊上下移動。
- 先不說Playable怎么實現,我都沒想起怎么用Animator實現這個效果,所以我先去翻了翻Animator相關資料。Animator實現主要靠在Animator Controller上加Layer。這里我先將原先的Controller起名為cube_1Layer,接着另外創建了一個Controller名為cube_2Layer,它有兩層Layer,Base Layer依然只有Rotate動畫,具體設置如下圖:
Layer1放了Move動畫,具體設置如下圖:
把Cube上的Animator Controller換為cube_2Layer,直接運行Unity即可看到Cube邊旋轉邊移動效果。這里通過修改Layer中的Weight,可以解答“【問題2】Animator怎樣控制權重”(更具體的可以參考這篇博文)。
- 怎么用Playable實現同樣效果呢?我沒有搜到直接的博文,但在回顧之前的“實現字幕軌”時看到了InputWeight,猜測應該會用到它,然后我試着寫出了能成功運行的腳本。最后,我在去看官方Manual——Playables API時發現官方早已寫好種種說明,包括前面的實現代碼,唯一少的就是具體例子。最開始的官方介紹博文“Playable API:定制你的動畫系統”需要的程序就在Manual這里,示例工程Simple Animation也應該對照Manual閱讀。和官方Manual相比,【測試1】參考的個人博客效果還不夠充分,圖中只有2種結點——Ouput和Clip,而按着Manual給的腳本,圖中會有3種結點——Ouput,Clip和Mixer,如下圖。
- 這里用的腳本如下,我除了實現播動畫功能外,還加了點擊按鈕停止播放的功能。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Animations; using UnityEngine.Audio; using UnityEngine.Playables; /// <summary> /// 這篇程序可以參考官方 https://docs.unity3d.com/ScriptReference/Playables.PlayableGraph.Connect.html /// </summary> [RequireComponent(typeof(Animator))] public class Playable_RotateMoveCube : MonoBehaviour { public AnimationClip animClipA; public AnimationClip animClipB; private PlayableGraph graph; private AnimationLayerMixerPlayable mixer; private AnimationPlayableOutput output; void Start() { graph = PlayableGraph.Create("RotateMoveCube"); output = AnimationPlayableOutput.Create(graph, "Animation", GetComponent<Animator>()); // inputCount 可以接幾個playable mixer = AnimationLayerMixerPlayable.Create(graph, 3); output.SetSourcePlayable(mixer); AnimationClipPlayable clipPlayA = AnimationClipPlayable.Create(graph, animClipA); AnimationClipPlayable clipPlayB = AnimationClipPlayable.Create(graph, animClipB); // sourceOutputPort 默認為0 graph.Connect(clipPlayA, 0, mixer, 0); graph.Connect(clipPlayB, 0, mixer, 1); // weight填0.5f只會有一半效果,和我理解的a + b + c = 1不同 mixer.SetInputWeight(0, 1f); mixer.SetInputWeight(1, 1f); graph.Play(); } public void ClickClose() { // ...這里暫無判空等檢測 graph.Disconnect(mixer, 0); graph.Disconnect(mixer, 1); graph.DestroyPlayable(mixer); graph.DestroyOutput(output); // cube位置會停在銷毀的時刻 } public void ClickStopB() { // ...這里暫無判空等檢測 mixer.SetInputWeight(1, 0f); //graph.Disconnect(mixer, 1); } void OnDisable() { graph.Destroy(); } }
- 在Cube上設置如下圖:
- 最后在運行時,還發現一個小問題:Move動作和Cube實際位置不一致,因為動作里記錄的是Cube世界坐標,再給Cube套個父物體就能解決這個問題(具體可參考這篇博文)。最后,運行效果如下圖。通過修改腳本中的InputWeight,可以解答“【問題2】Playable怎樣控制權重”。
- 這里的Graph視圖如下:
【測試3】Clip和Controller混用
- 為了解答“【問題3】Clip怎樣和Animator混用”,實現要用到的腳本依然在Manual上可以找到,如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Animations; using UnityEngine.Playables; [RequireComponent(typeof(Animator))] public class Playable_BlendController : MonoBehaviour { public AnimationClip clip; public RuntimeAnimatorController controller; public float weight; private PlayableGraph graph; private AnimationMixerPlayable mixer; void Start() { graph = PlayableGraph.Create("BlendController"); AnimationPlayableOutput output = AnimationPlayableOutput.Create(graph, "Animation", GetComponent<Animator>()); mixer = AnimationMixerPlayable.Create(graph, 2); output.SetSourcePlayable(mixer); AnimationClipPlayable clipP = AnimationClipPlayable.Create(graph, clip); AnimatorControllerPlayable ctrlP = AnimatorControllerPlayable.Create(graph, controller); graph.Connect(clipP, 0, mixer, 0); graph.Connect(ctrlP, 0, mixer, 1); graph.Play(); } void Update() { weight = Mathf.Clamp01(weight); mixer.SetInputWeight(0, 1.0f - weight); mixer.SetInputWeight(1, weight); } void OnDisable() { graph.Destroy(); } }
- 因為用到的Controller是前面Cube的cube_1Layer,所以這次我選擇在Capsule上掛這個腳本,具體設置如下圖:
- 運行時調節Inspector面板上的Weight值,可以看到Capsule變化。以下是將Weight調為0.66時的效果。“【問題3】Clip怎樣和Animator混用”解答完畢。
- 這里的Graph視圖如下:
- 通過上面一系列測試,我對Graph有了一定了解。對於“【問題1】 Playable Graph 怎么用”,我的解答是:像它的名字一樣,當一張圖來用,在圖上,可以找到我需要的任何節點。
回到示例工程Simple Animation
- 在完成上面測試后,對着Manual再次閱讀官方介紹博文“Playable API:定制你的動畫系統”,現在是時候回到示例工程Simple Animation了。看看效果,翻翻代碼,我能明白示例工程這3個場景到底要告訴我什么了。
-
場景PlayOnce 演示重點:簡單利用Playable播放一個cube旋轉動畫;同時和Legacy、Mecanim比較。
-
場景Complex演示重點:簡單利用Playable播放一個人形動畫;同時和Legacy、Mecanim比較。
-
場景ComplexWithTransitions演示重點:簡單利用Playable播放人形動畫A,然后直接粗暴切換到動畫B;同時和Legacy、Mecanim比較。
-
沒有提及的
- Manual上還有很多功能例如控制時間,按序列播放動畫,這里都沒有展開,我把它們的具體實現都一起放在測試工程里了。
下載
- 我的測試工程G站鏈接