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);
            else
                mixer.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

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值