本文主要是介绍Unity流水账6:Playables API,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1.Playables API
Playables API提供了一种通过组织和评估PlayableGraph的树状结构中的数据源来创建工具,效果或其他游戏机制的方法。PlayableGraph允许你mix,blend和修改多个数据源,并通过单个输出播放它们。
Playables API支持动画,音频和脚本。Playables API还提供了通过脚本与animation系统和audio系统交互的能力。
尽管Playables API目前仅限于动画,音频和脚本,但它是一种通用API,最终将被视频和其他系统使用。
(1).Playable vs Animation
动画系统已经有了一个图形编辑器,它是一个仅限于播放动画的状态机系统。Playables API旨在更灵活,并支持其他系统。Playables API还允许创建状态机无法实现的图形。这些图表表示数据流,指示每个节点生成和消耗的内容。此外,单个图不限于单个系统。单个图可能包含动画、音频和脚本的节点
(2).使用Playables API的优点
·Playables API允许动态动画混合。这意味着场景中的对象可以提供自己的动画。例如:武器,箱子和陷阱的动画可以动态添加到PlayableGraph并使用一段时间。
·Playables API允许你轻松播放单个动画,而无需创建和管理AnimatorController asset所需的开销。
·Playables API允许用户动态创建混合图并直接逐帧控制混合权重。
·可以在运行时创建PlayableGraph,根据条件根据需要添加可播放节点。PlayableGraph可以根据当前情况的要求进行定制,而不是具有启用和禁用节点的巨大的"one-size-fit-all"的图形
2.PlayableGraph
PlayableGraph定义了一组绑定到GameObject或组件的可播放输出。PlayableGraph还定义了一组Playables及其关系。下图提供了一个示例。
PlayableGraph负责其playables及其输出的生命周期。使用PlayableGraph创建,连接和销毁playables
[外链图片转存失败(img-cv9kmacZ-1562143025664)(https://docs.unity3d.com/uploads/Main/PlayablesGraph0.png)]
在图一中,当显示PlayableGraph时,从图形节点的名称中删除术语"Playable"以使其更紧凑。例如:名为"AnimationClipPlayable"的节点显示为"AnimationClip"。
[外链图片转存失败(img-vASOOFP8-1562143025665)(https://docs.unity3d.com/uploads/Main/PlayablesGraphWarning.png)]
playable是实现IPlayable接口的c#结构。它用于定义与其他playable的关系。同样playable output是实现IPlayableOutput的c#结构,用于定义PlayableGraph的输出。
下图显示了最常见的playable类型。
[外链图片转存失败(img-vLRzBWf4-1562143025670)(https://docs.unity3d.com/uploads/Main/PlayablesGraph1.png)]
下图显示了playable output类型。
[外链图片转存失败(img-SxQqyyKt-1562143025671)(https://docs.unity3d.com/uploads/Main/PlayablesGraph2.png)]
playable类型以及playable output类型实现为c#结构,以避免为垃圾收集(gc)分配内存。
Playable是所有playables的基本类型,这意味着你可以随时隐式地将playables类型投射到它上面。相反则不行,如果将Playable显式转换为不兼容的类型,则会抛出异常。它还定义了可在playable上执行的所有基本方法。要访问特定于类型的方法,需要将playable转换为适当的类型。
"PlayableOutput"也是如此,它是所有可播放输出的基本类型,它定义了基本方法。
注意:Playable和PlayableOutput不会暴露很多方法。PlayableExtensions和PlayableOutputExtensions静态类提供了扩展方法。
所有非抽象的playables都有一个公共静态方法Create(),它创建相应类型的playable。Create()方法总是将PlayableGraph作为其第一个参数,该图表拥有新创建的playable.某些类型的playable可能需要附加参数。非抽象playable outputs也公开了Create()方法
有效的playable output应连接到playable。如果playable output没有连接到playable,则playable output不执行任何操作。要将playable output 连接到playable,可使用PlayableOutput.SetSourcePlayable()方法。被连接的playable充当playable树的根,用于该特定的playable output.
要将两个playable连接在一起,可使用PlayableGraph.Connect()方法。注意:某些playables没有input
使用PlayableGraph.Create()静态方法创建PlayableGraph.
使用PlayableGraph.Play()方法播放PlayableGraph。
使用PlayableGraph.Stop()方法停止播放PlayableGraph
使用PlayableGraph.Evaluate()方法在特定时间评估PlayableGraph的状态。
使用PlayableGraph.Destroy()方法手动销毁PlayableGraph。此方法会自动销毁PlayableGraph创建的所有playable和playable output。你必须手动调用此Destroy方法来销毁PlayableGraph,否则Unity会发出错误消息。
3.ScriptPlayable以及PlayableBehaviour
要创建自定义的playable,必须继承PlayableBehaviour基类。
public class MyCustomPlayableBehaviour:PlayableBehaviour
{//实现自定义的playable行为,根据需要覆盖PlayableBehaviour方法。
}
要将PlayableBehaviour用作自定义playable,它还必须封装在ScriptPlayable<>对象中。如果你没有自定义playable的实例,可以通过调用以下内容为对象创建ScriptPlayable<>:
ScriptPlayable<MyCustomPlayableBehaviour>.Create(playableGraph)
如果你已经有用自定义playable的实例,则可以通过调用以下内容将其包装为ScriptPlayable<>:
MyCustomPlayableBehaviour myPlayable = new MyCustomPlayableBehaviour();
ScriptPlayable<MyCustomPlayableBehaviour>.Create(playableGraph,myPlayable);
在这种情况下,在将实例分配给ScriptPlayable<>之前克隆该实例。实际上,此代码与前面的代码完全相同,区别在于myPlayable可以是在Inspector配置的公共属性,然后你可以为脚本的每个实例设置行为。
你可以使用ScriptPlayable.GetBehaviour()方法从ScriptPlayable<>获取PlayableBehaviour对象。
4.Playables示例
(1).PlayableGraph Visualizer
本文档中的所有示例都使用PlayableGraph Visualizer(如下图所示)来说明Playables API创建的树和节点。PlayableGraph Visualizer是一个工具可通过Github下载。
使用PlayableGraph Visualizer的步骤
1.从GitHub存储库(【PlayableGraph Visualizer下载链接】)下载与你的Unity版本对应的PlayableGraph Visualizer.
2.选择Window->PlayableGraph Visualizer打开该工具。
3.使用GraphVisualizerClient.Show(PlayableGraph graph,string name)注册图形
[外链图片转存失败(img-S6smrpjm-1562143025673)(https://docs.unity3d.com/uploads/Main/PlayablesExamples5.png)]
图中的Playables由彩色节点表示。线颜色强度表示混合的权重。
(2).在GameObject上播放单个动画片段
此示例演示了一个简单的PlayableGraph,其中包含一个playable output,该playable output连接到单个playable.playable播放单个animation clip(clip).AnimationClipPlayable必须包装animation clip以使其与Playaables API兼容
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;[RequireComponent(typeof(Animator))]
public class PlayAnimationSample : MonoBehaviour
{public AnimationClip clip;PlayableGraph playableGraph;private void Start(){playableGraph = PlayableGraph.Create();playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());var clipPlayable = AnimationClipPlayable.Create(playableGraph, clip);playableOutput.SetSourcePlayable(clipPlayable);playableGraph.Play();}private void OnDisable(){playableGraph.Destroy();}
}
[外链图片转存失败(img-dLBT3k6k-1562143025674)(https://docs.unity3d.com/uploads/Main/PlayablesExamples0.png)]
使用AnimationPlayableUtilityies简化animation playables的创建和播放,如以下示例所示:
using UnityEngine;
using UnityEngine.Playables;[RequireComponent(typeof(Animator))]
public class PlayAnimationUtilitiesSample : MonoBehaviour
{public AnimationClip clip;PlayableGraph playableGraph;private void Start(){AnimationPlayableUtilities.PlayClip(GetComponent<Animator>(), clip, out playableGraph);}private void OnDisable(){playableGraph.Destroy();}
}
(3).创建animation blend tree
此示例演示如何使用AnimationMixerPlayable混合两个Animation Clips.在混合Animation Clip之前,他们必须由playable包装。为此AnimationClipPlayable(clipPlayable0和clipPlayable1)包装每个Animation Clip(clip0和clip1).SetInputWeight()方法动态调整每个playable的混合权重。
虽然在此示例中未显示,但你也可以使用AnimationMixerPlayable混合playable mixers和其他playables.
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(){playableGraph = PlayableGraph.Create();var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);playableOutput.SetSourcePlayable(mixerPlayable);var clipPlayable0 = AnimationClipPlayable.Create(playableGraph, clip0);var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip1);playableGraph.Connect(clipPlayable0, 0, mixerPlayable, 0);playableGraph.Connect(clipPlayable1, 0, mixerPlayable, 1);playableGraph.Play();}private void Update(){weight = Mathf.Clamp01(weight);mixerPlayable.SetInputWeight(0, 1.0f - weight);mixerPlayable.SetInputWeight(1, weight);}private void OnDisable(){playableGraph.Destroy();}
}
[外链图片转存失败(img-Ywp0BGnW-1562143025675)(https://docs.unity3d.com/uploads/Main/PlayablesExamples1.png)]
(4).混合AnimationClip和AnimatorController
此示例演示如何使用AnimationMixerPlayable将AnimationClip与AnimatorController混合
在混合AnimationClip和AnimatorController之前,它们必须由playables包装。为此,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;private void Start(){playableGraph = PlayableGraph.Create();var playableOutPut = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);playableOutPut.SetSourcePlayable(mixerPlayable);var clipPlayable = AnimationClipPlayable.Create(playableGraph, clip);var ctrlPlayable = AnimatorControllerPlayable.Create(playableGraph, controller);playableGraph.Connect(clipPlayable, 0, mixerPlayable, 0);playableGraph.Connect(ctrlPlayable, 0, mixerPlayable, 1);playableGraph.Play();}private void Update(){weight = Mathf.Clamp01(weight);mixerPlayable.SetInputWeight(0, 1.0f - weight);mixerPlayable.SetInputWeight(1, weight);}private void OnDisable(){playableGraph.Destroy();}
}
(5).创建具有多个输出的PlayableGraph
此示例演示如何使用两种不同的Playable创建PlayableGraph:AudioPlayableOutput和AnimationPlayableOutput。PlayableGraph可以有许多不同类型的Playable。
此示例还演示了如何通过连接到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;private void Start(){playableGraph = PlayableGraph.Create();var animationOutPut = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());var audioOutput = AudioPlayableOutput.Create(playableGraph, "Audio", GetComponent<AudioSource>());var animationClipPlayable = AnimationClipPlayable.Create(playableGraph, animationClip);var audioClipPlayable = AudioClipPlayable.Create(playableGraph, audioClip, true);animationOutPut.SetSourcePlayable(animationClipPlayable);audioOutput.SetSourcePlayable(audioClipPlayable);playableGraph.Play();}private void OnDisable(){playableGraph.Destroy();}
}
[外链图片转存失败(img-973Fo4Ff-1562143025680)(https://docs.unity3d.com/uploads/Main/PlayablesExamples2.png)]
(6).控制树的播放状态
此示例演示如何使用Playable.SetPlayState()方法控制PlayableGraph树上节点的播放状态。SetPlayState方法控制整个树,其中一个分支或单个节点的播放状态。
当设置节点的播放状态时,状态将传播到其所有子节点,而不管它们的播放状态如何。例如:如果明确暂停子节点,则父节点设置为"playing"也会将其所有子节点设置为"playing"
在此示例中,PlayableGraph包含混合两个animation clips的mixer。AnimationClipPlayable包装每个animation clip。SetPlayState()方式显示暂停第二个playable。第二个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;private void Start(){playableGraph = PlayableGraph.Create();var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);playableOutput.SetSourcePlayable(mixerPlayable);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.SetPlayState(PlayState.Paused);playableGraph.Play();}private void OnDisable(){playableGraph.Destroy();}
}
[外链图片转存失败(img-EAKwTgpC-1562143025680)(https://docs.unity3d.com/uploads/Main/PlayablesExamples3.png)]
(7).控制树的时间
此示例演示如何使用Play()方法播放PlayableGraph,如何使用SetPlayState()方法暂停playable,以及如何使用SetTime()方法手动设置playable的本地时间和变量。
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;private void Start(){playableGraph = PlayableGraph.Create();var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());playableClip = AnimationClipPlayable.Create(playableGraph, clip);playableOutput.SetSourcePlayable(playableClip);playableGraph.Play();playableClip.SetPlayState(PlayState.Paused);}private void Update(){playableClip.SetTime(time);}private void OnDisable(){playableGraph.Destroy();}
}
(8).创建PlayableBehaviour
此示例演示如何使用PlayableBehaviour公共类创建自定义Playable。此示例还演示了如何覆盖PrepareFrame()虚函数以控制PlayableGraph上的节点。自定义Playable可覆盖PlayableBehaviour类的任何其他虚函数.
在此示例中,受控节点是一系列Animation Clips(clipsToPlay).SetInputMethod()修改每个Animation Clip的混合权重,确保一次只播放一个片段,SetTime()方法调整本地时间,以便在激活Animation Clip时开始播放。
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;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);currentClip.SetTime(0);m_TimeToNextClip = currentClip.GetAnimationClip().length;}for(int clipIndex = 0; clipIndex < mixer.GetInputCount(); ++clipIndex){if (clipIndex == m_CurrentClipIndex)mixer.SetInputWeight(clipIndex, 1.0f);elsemixer.SetInputWeight(clipIndex, 0.0f);}}
}
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;[RequireComponent(typeof(Animator))]
public class PlayQueueSample : MonoBehaviour
{public AnimationClip[] clipsToPlay;PlayableGraph playableGraph;private 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();}private void OnDisable(){playableGraph.Destroy();}
}
[外链图片转存失败(img-3ogg76kh-1562143025682)(https://docs.unity3d.com/uploads/Main/PlayablesExamples4.png)]
参考资料:
https://docs.unity3d.com/Manual/Playables.html
【UNITY3D Playables API 实践(上)】https://vrast.cn/posts/5df16bd2/
【UNITY3D Playables API实践(下)】http://keylexiao.github.io/posts/323c9fde/
http://gad.qq.com/article/detail/33662
https://blogs.unity3d.com/cn/2017/08/02/unity-2017-1-feature-spotlight-playable-api/
【PlayableGraph Visualizer下载链接】https://github.com/Unity-Technologies/graph-visualizer
这篇关于Unity流水账6:Playables API的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!