本页面的示例使用可播放图可视化器来说明由 可播放提供一种通过在称为可播放图的树状结构中组织和评估数据源来创建工具、效果或其他游戏机制的方法的 API。 更多信息
请参阅 词汇表 API 创建的树和节点。可播放图可视化器是通过 GitHub 提供的工具。
注意:可播放图可视化器是一个已停产的实验性软件包,可能无法与您的 Unity 版本兼容。
要使用可播放图可视化器
从 GitHub 存储库 下载可播放图可视化器。
通过选择窗口 > 可播放图可视化器 打开工具。
使用 GraphVisualizerClient.Show(PlayableGraph graph, string name)
注册您的图。
图中的可播放由彩色节点表示。线颜色强度指示混合的权重。有关可播放图可视化器的更多信息,请参阅 GitHub。
此示例演示了一个简单的 PlayableGraph
,它具有单个可播放输出,该输出链接到单个可播放节点。可播放节点播放单个 动画剪辑可用于动画角色或简单动画的动画数据。它是运动的简单“单元”部分,例如(一个特定实例的)“闲置”、“行走”或“奔跑”。 更多信息
请参阅 词汇表(剪辑)。AnimationClipPlayable
必须包装动画剪辑,使其与可播放 API 兼容。
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class PlayAnimationSample : MonoBehaviour
{
public AnimationClip clip;
PlayableGraph playableGraph;
void Start()
{
playableGraph = PlayableGraph.Create();
playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
// Wrap the clip in a playable.
var clipPlayable = AnimationClipPlayable.Create(playableGraph, clip);
// Connect the Playable to an output.
playableOutput.SetSourcePlayable(clipPlayable);
// Plays the Graph.
playableGraph.Play();
}
void OnDisable()
{
// Destroys all Playables and PlayableOutputs created by the graph.
playableGraph.Destroy();
}
}
使用 AnimationPlayableUtilities
简化动画可播放的创建和播放,如以下示例所示
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class PlayAnimationUtilitiesSample : MonoBehaviour
{
public AnimationClip clip;
PlayableGraph playableGraph;
void Start()
{
AnimationPlayableUtilities.PlayClip(GetComponent<Animator>(), clip, out playableGraph);
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
此示例演示如何使用 AnimationMixerPlayable
混合两个动画剪辑。在混合动画剪辑之前,必须用可播放进行包装。为此,AnimationClipPlayable
(clipPlayable0 和 clipPlayable1)分别包装每个 AnimationClip
(clip0 和 clip1)。SetInputWeight()
方法动态调整每个可播放的混合权重。
尽管在本示例中未显示,但您也可以使用 AnimationMixerPlayable
混合可播放混合器和其他可播放。
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class MixAnimationSample : MonoBehaviour
{
public AnimationClip clip0;
public AnimationClip clip1;
public float weight;
PlayableGraph playableGraph;
AnimationMixerPlayable mixerPlayable;
void Start()
{
// Creates the graph, the mixer and binds them to the Animator.
playableGraph = PlayableGraph.Create();
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);
playableOutput.SetSourcePlayable(mixerPlayable);
// Creates AnimationClipPlayable and connects them to the mixer.
var clipPlayable0 = AnimationClipPlayable.Create(playableGraph, clip0);
var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip1);
playableGraph.Connect(clipPlayable0, 0, mixerPlayable, 0);
playableGraph.Connect(clipPlayable1, 0, mixerPlayable, 1);
// Plays the Graph.
playableGraph.Play();
}
void Update()
{
weight = Mathf.Clamp01(weight);
mixerPlayable.SetInputWeight(0, 1.0f-weight);
mixerPlayable.SetInputWeight(1, weight);
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
此示例演示如何使用 AnimationMixerPlayable
混合 AnimationClip
和 AnimatorController
。
在混合 AnimationClip
和 AnimatorController
之前,必须用可播放进行包装。为此,AnimationClipPlayable
(clipPlayable)包装 AnimationClip
(clip),而 AnimatorControllerPlayable
(ctrlPlayable)包装 RuntimeAnimatorController(controller)。SetInputWeight()
方法动态调整每个可播放的混合权重。
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class RuntimeControllerSample : MonoBehaviour
{
public AnimationClip clip;
public RuntimeAnimatorController controller;
public float weight;
PlayableGraph playableGraph;
AnimationMixerPlayable mixerPlayable;
void Start()
{
// Creates the graph, the mixer and binds them to the Animator.
playableGraph = PlayableGraph.Create();
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);
playableOutput.SetSourcePlayable(mixerPlayable);
// Creates AnimationClipPlayable and connects them to the mixer.
var clipPlayable = AnimationClipPlayable.Create(playableGraph, clip);
var ctrlPlayable = AnimatorControllerPlayable.Create(playableGraph, controller);
playableGraph.Connect(clipPlayable, 0, mixerPlayable, 0);
playableGraph.Connect(ctrlPlayable, 0, mixerPlayable, 1);
// Plays the Graph.
playableGraph.Play();
}
void Update()
{
weight = Mathf.Clamp01(weight);
mixerPlayable.SetInputWeight(0, 1.0f-weight);
mixerPlayable.SetInputWeight(1, weight);
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
此示例演示如何创建具有两种不同可播放输出类型的 PlayableGraph
:AudioPlayableOutput
和 AnimationPlayableOutput
。PlayableGraph
可以具有多种不同类型的可播放输出。
此示例还演示了如何通过连接到 AudioPlayableOutput
的 AudioClipPlayable
播放 AudioClip
。
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Audio;
using UnityEngine.Playables;
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(AudioSource))]
public class MultiOutputSample : MonoBehaviour
{
public AnimationClip animationClip;
public AudioClip audioClip;
PlayableGraph playableGraph;
void Start()
{
playableGraph = PlayableGraph.Create();
// Create the outputs.
var animationOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
var audioOutput = AudioPlayableOutput.Create(playableGraph, "Audio", GetComponent<AudioSource>());
// Create the playables.
var animationClipPlayable = AnimationClipPlayable.Create(playableGraph, animationClip);
var audioClipPlayable = AudioClipPlayable.Create(playableGraph, audioClip, true);
// Connect the playables to an output.
animationOutput.SetSourcePlayable(animationClipPlayable);
audioOutput.SetSourcePlayable(audioClipPlayable);
// Plays the Graph.
playableGraph.Play();
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
此示例演示如何控制 PlayableGraph
树上节点的播放状态。Pause()
和 Play()
方法控制整个树、其中一个分支或单个节点的播放状态。
设置节点的播放状态时,状态会传播到其所有子级,无论其播放状态如何。例如,如果子节点被显式暂停,将父节点设置为播放也会将所有子节点设置为播放。
在本示例中,PlayableGraph
包含一个混合器,该混合器混合两个动画剪辑。AnimationClipPlayable
包装每个动画剪辑,而 Pause()
方法显式暂停第二个可播放。第二个 AnimationClipPlayable
被显式暂停,因此其内部时间不会前进,并输出相同的值。确切的值取决于 AnimationClipPlayable
被暂停时的特定时间。
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class PauseSubGraphAnimationSample : MonoBehaviour
{
public AnimationClip clip0;
public AnimationClip clip1;
PlayableGraph playableGraph;
AnimationMixerPlayable mixerPlayable;
void Start()
{
// Creates the graph, the mixer and binds them to the Animator.
playableGraph = PlayableGraph.Create();
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);
playableOutput.SetSourcePlayable(mixerPlayable);
// Creates AnimationClipPlayable and connects them to the mixer.
var clipPlayable0 = AnimationClipPlayable.Create(playableGraph, clip0);
var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip1);
playableGraph.Connect(clipPlayable0, 0, mixerPlayable, 0);
playableGraph.Connect(clipPlayable1, 0, mixerPlayable, 1);
mixerPlayable.SetInputWeight(0, 1.0f);
mixerPlayable.SetInputWeight(1, 1.0f);
clipPlayable1.Pause();
// Plays the Graph.
playableGraph.Play();
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
此示例演示了如何使用 Play()
方法播放 PlayableGraph,如何使用 Pause()
方法暂停可播放,以及如何使用 SetTime()
方法使用变量手动设置可播放的本地时间。
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
[RequireComponent(typeof(Animator))]
public class PlayWithTimeControlSample : MonoBehaviour
{
public AnimationClip clip;
public float time;
PlayableGraph playableGraph;
AnimationClipPlayable playableClip;
void Start()
{
playableGraph = PlayableGraph.Create();
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
// Wrap the clip in a playable.
playableClip = AnimationClipPlayable.Create(playableGraph, clip);
// Connect the Playable to an output.
playableOutput.SetSourcePlayable(playableClip);
// Plays the Graph.
playableGraph.Play();
// Stops time from progressing automatically.
playableClip.Pause();
}
void Update ()
{
// Control the time manually.
playableClip.SetTime(time);
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}
此示例演示了如何使用 PlayableBehaviour
公共类创建自定义可播放。此示例还演示了如何重写 PrepareFrame()
虚拟方法来控制 PlayableGraph
上的节点。自定义可播放可以重写 PlayableBehaviour
类中的任何其他虚拟方法。
在本示例中,正在控制的节点是一系列动画剪辑 (clipsToPlay)。SetInputMethod()
修改每个动画剪辑的混合权重,确保一次只播放一个剪辑,而 SetTime()
方法调整本地时间,以便播放从激活动画剪辑的时刻开始。
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
public class PlayQueuePlayable : PlayableBehaviour
{
private int m_CurrentClipIndex = -1;
private float m_TimeToNextClip;
private Playable mixer;
public void Initialize(AnimationClip[] clipsToPlay, Playable owner, PlayableGraph graph)
{
owner.SetInputCount(1);
mixer = AnimationMixerPlayable.Create(graph, clipsToPlay.Length);
graph.Connect(mixer, 0, owner, 0);
owner.SetInputWeight(0, 1);
for (int clipIndex = 0 ; clipIndex < mixer.GetInputCount() ; ++clipIndex)
{
graph.Connect(AnimationClipPlayable.Create(graph, clipsToPlay[clipIndex]), 0, mixer, clipIndex);
mixer.SetInputWeight(clipIndex, 1.0f);
}
}
override public void PrepareFrame(Playable owner, FrameData info)
{
if (mixer.GetInputCount() == 0)
return;
// Advance to next clip if necessary.
m_TimeToNextClip -= (float)info.deltaTime;
if (m_TimeToNextClip <= 0.0f)
{
m_CurrentClipIndex++;
if (m_CurrentClipIndex >= mixer.GetInputCount())
m_CurrentClipIndex = 0;
var currentClip = (AnimationClipPlayable)mixer.GetInput(m_CurrentClipIndex);
// Reset the time so that the next clip starts at the correct position.
currentClip.SetTime(0);
m_TimeToNextClip = currentClip.GetAnimationClip().length;
}
// Adjust the weight of the inputs.
for (int clipIndex = 0 ; clipIndex < mixer.GetInputCount(); ++clipIndex)
{
if (clipIndex == m_CurrentClipIndex)
mixer.SetInputWeight(clipIndex, 1.0f);
else
mixer.SetInputWeight(clipIndex, 0.0f);
}
}
}
[RequireComponent(typeof (Animator))]
public class PlayQueueSample : MonoBehaviour
{
public AnimationClip[] clipsToPlay;
PlayableGraph playableGraph;
void Start()
{
playableGraph = PlayableGraph.Create();
var playQueuePlayable = ScriptPlayable<PlayQueuePlayable>.Create(playableGraph);
var playQueue = playQueuePlayable.GetBehaviour();
playQueue.Initialize(clipsToPlay, playQueuePlayable, playableGraph);
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
playableOutput.SetSourcePlayable(playQueuePlayable);
playableOutput.SetSourceInputPort(0);
playableGraph.Play();
}
void OnDisable()
{
// Destroys all Playables and Outputs created by the graph.
playableGraph.Destroy();
}
}