【Unity】Timeline探索記(3)關於Playable


寫在前面

  • 在前幾次探索中,時不時都會看到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上還有很多功能例如控制時間,按序列播放動畫,這里都沒有展開,我把它們的具體實現都一起放在測試工程里了。

下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM