???Playable API???是一個強大的工具,用于更靈活地控制動畫、音頻、腳本等時間軸內容的播放和混合。它提供了比傳統 Animator 更底層、更可控的方式管理時間軸行為,尤其適合復雜動畫邏輯或動態內容組合的場景。
優點:
1.Playables API 支持動態動畫混合,這意味著場景中的對象可以提供自己的動畫。例如,武器、寶箱和陷阱的動畫可以動態添加到 PlayableGraph 并使用一段時間。
2.Playables API 可播放單個動畫,而不會產生創建和管理 AnimatorController 資源所涉及的開銷。
3.Playables API 允許用戶動態創建混合圖并直接逐幀控制混合權重。
4.可在運行時創建 PlayableGraph,根據條件按需添加可播放節點。可量身定制 PlayableGraph 來適應當前情況的要求,而不是提供一個巨大的“一刀切”圖形來啟用和禁用節點。
核心機制:
核心類型??:
1.??PlayableGraph
動畫控制的核心容器,負責管理所有動畫節點(Playable)和輸出通道(PlayableOutput)。
PlayableGraph graph = PlayableGraph.Create("MyAnimationGraph");
2.??Playable??:
??所有可播放項的基類型??(如AnimationClipPlayable),使用struct實現??避免內存分配??。
??隱式轉換??子類型為Playable,但??反向需顯式轉換??(可能因類型不兼容拋出異常)。
3.PlayableOutput??:
輸出的基類型??(如AnimationPlayableOutput),同樣為struct。
必須通過SetSourcePlayable()??鏈接到Playable??,否則無效果。
AnimationPlayableOutput output = AnimationPlayableOutput.Create(graph, "AnimationOutput", animator);
注:Playable 和 PlayableOutput 未暴露大量方法。可使用PlayableExtensions和PlayableOutputExtensions靜態類提供的擴展方法。
創建與連接??:??
1.創建可播放項/輸出??
所有非抽象類型提供??靜態Create()方法??,首個參數為PlayableGraph(擁有該節點)。
var clipPlayable = AnimationClipPlayable.Create(graph, clip);
var output = AnimationPlayableOutput.Create(graph, "Output", animator);
2.??連接節點??
??節點間連接??:通過PlayableGraph.Connect(source, sourcePort, target, targetPort)。
??輸出綁定根節點??:output.SetSourcePlayable(rootPlayable)。
PlayableGraph管理??
??1.生命周期??
??創建??:PlayableGraph.Create("GraphName")。
??播放/停止??:graph.Play() / graph.Stop()。
??手動更新??:graph.Evaluate(deltaTime)(適用于非實時更新)。
??銷毀??:??必須手動調用??graph.Destroy(),否則報錯(自動銷毀其下所有節點)。
2.??注意事項??
??輸入限制??:某些Playable類型??不支持輸入連接??(如AnimationClipPlayable)。
??權重控制??:混合節點需通過SetInputWeight()管理權重。
??內存安全??:避免頻繁創建/銷毀,優先重用節點。
使用:
1.播放單個動畫
在角色物體掛載以下腳本,如圖:
代碼:
[RequireComponent(typeof(Animator))]
public class SimplePlayable: MonoBehaviour
{public Animator animator;public AnimationClip clip;private PlayableGraph graph;void Start(){graph = PlayableGraph.Create();this.CreateSimpleAnimation();graph.Play();}void OnDestroy(){if (graph.IsValid())graph.Destroy();}void CreateSimpleAnimation(){// 創建AnimationClipPlayablevar clipPlayable = AnimationClipPlayable.Create(graph, clip);// 創建輸出并連接到Animatorvar output = AnimationPlayableOutput.Create(graph, "Output", animator);output.SetSourcePlayable(clipPlayable);}
}
結果:
2.混合兩個動畫(Mixer)?
在角色物體上掛載以下腳本,如圖:
代碼:
[RequireComponent(typeof(Animator))]
public class MixerPlayable : MonoBehaviour
{public AnimationClip clip0;public AnimationClip clip1;[Range(0f, 1f)] public float weight;PlayableGraph playableGraph;AnimationMixerPlayable mixerPlayable;void Start(){// 創建該圖和混合器,然后將它們綁定到 Animator。playableGraph = PlayableGraph.Create();var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2); //2個輸入playableOutput.SetSourcePlayable(mixerPlayable);// 創建 AnimationClipPlayable 并將它們連接到混合器。var clipPlayable0 = AnimationClipPlayable.Create(playableGraph, clip0);var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip1);// 連接動畫到MixerplayableGraph.Connect(clipPlayable0, 0, mixerPlayable, 0);playableGraph.Connect(clipPlayable1, 0, mixerPlayable, 1);//播放該圖。playableGraph.Play();}void Update(){// 設置混合權重(0表示全clip0,1表示全clip1)weight = Mathf.Clamp01(weight);mixerPlayable.SetInputWeight(0, 1.0f - weight);mixerPlayable.SetInputWeight(1, weight);}void OnDisable(){//銷毀該圖創建的所有可播放項和輸出。playableGraph.Destroy();}
}
結果:
3.分層動畫(LayerMixer)?
創建一個動畫遮罩
設置遮罩,如下圖,將疊加動畫的下半動畫不播放
將角色物體上掛載以下腳本,如下圖:
代碼:
[RequireComponent(typeof(Animator))]
public class LayerMixerPlayable : MonoBehaviour
{public AnimationClip runClip;public AnimationClip attackClip;public AvatarMask attackMask;PlayableGraph graph;void Start(){// 創建該圖和混合器,然后將它們綁定到 Animator。graph = PlayableGraph.Create();var playableOutput = AnimationPlayableOutput.Create(graph, "Animation", GetComponent<Animator>());AnimationLayerMixerPlayable layerMixer = AnimationLayerMixerPlayable.Create(graph, 2);playableOutput.SetSourcePlayable(layerMixer);// 基礎層(如移動)var baseLayer = AnimationClipPlayable.Create(graph, runClip);graph.Connect(baseLayer, 0, layerMixer, 0);layerMixer.SetInputWeight(0, 1f);// 疊加層(如攻擊)var attackLayer = AnimationClipPlayable.Create(graph, attackClip);graph.Connect(attackLayer, 0, layerMixer, 1);layerMixer.SetInputWeight(1, 1f);// 設置層級遮罩(可選)layerMixer.SetLayerMaskFromAvatarMask(1, attackMask); // 僅特定身體部位播放攻擊動畫graph.Play();}
}
結果:將一個跑的動畫和一個攻擊動畫混合,再將攻擊動畫的下半身添加動畫遮罩
4.動態創建與銷毀節點
// 動態添加新動畫
public void AddDynamicAnimation(AnimationClip clip)
{var newClipPlayable = AnimationClipPlayable.Create(graph, clip);mixer.AddInput(newClipPlayable, 0, 1f); // 假設mixer已存在
}// 銷毀節點
public void RemoveAnimation(int index)
{mixer.GetInput(index).Destroy();mixer.DisconnectInput(index);
}
創建自定義可播放項:
??1. 創建自定義 Playable????
繼承 PlayableBehaviour??:定義自定義邏輯需從基類 PlayableBehaviour 派生,重寫其方法(如 PrepareFrame, OnPlayableCreate 等)。
public class MyCustomPlayableBehaviour : PlayableBehaviour
{// 實現自定義邏輯(如重寫幀更新方法)public override void PrepareFrame(Playable playable, FrameData info) {// 每幀執行邏輯}
}
可視化工具的安裝與使用:
性能優化
1.提前創建PlayableGraph??:在Awake()或Start()中初始化,避免運行時卡頓。
??2.重用Playable節點:??對于頻繁切換的動畫(如攻擊、受傷),預先創建節點并通過權重控制顯隱,而非反復創建/銷毀。
??3.限制更新頻率?:?若動畫無需每幀更新,可通過graph.Evaluate(deltaTime)手動控制更新。
??4.使用Playable TraversalMode??,設置遍歷模式優化性能:
graph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
graph.SetPlayableTraversalMode(PlayableTraversalMode.Passthrough);
應用場景
??1.角色移動混合??:根據速度動態混合走、跑、沖刺動畫。
??2.受傷動畫疊加??:在基礎動畫上疊加受傷抖動,不影響其他身體部位。
??3.過場動畫控制??:結合Timeline和Playable API實現復雜的過場動畫序列。
注意事項
1.銷毀PlayableGraph??:在對象銷毀時調用graph.Destroy(),防止內存泄漏。
2.??動畫長度處理:??循環動畫需手動控制停止,或使用ClipPlayable.SetDuration()。
3.??權重歸一化??:混合時確保權重總和不超過1,避免動畫異常。
4.??版本兼容性:??Playable API在Unity 2017.1+中穩定,但部分功能(如ScriptPlayable<T>)可能需要更新版本。
未完待續。。。
參考鏈接:
Playables API - Unity 手冊
Unity - 手冊:Playables API (unity3d.com)