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
    评论
这个错误通常是由于 Gradle 构建过程中出现了错误导致的。以下是一些可能的解决方法: 1. 清理 Gradle 缓存 在 Unity 中打开 Build Settings 窗口,点击 Player Settings 按钮,进入 Player Settings 界面。在 Android Settings 标签页中,找到 Other Settings 部分,将 "Custom Gradle Template" 选项取消勾选。然后在 Build Settings 窗口中选择 Android 平台,点击 "Export" 按钮,导出 Android 项目。然后在 Android Studio 中打开导出的项目,选择 "File" -> "Invalidate Caches / Restart...",清理 Gradle 缓存。然后重新构建 APK。 2. 升级 Gradle 版本 在 Unity 中打开 Build Settings 窗口,选择 Android 平台,点击 "Player Settings" 按钮,进入 Player Settings 界面。在 Android Settings 标签页中,找到 "Publishing Settings" 部分,在 "Gradle Version" 中选择最新版本的 Gradle。然后重新构建 APK。 3. 检查依赖项 在 Unity 中打开 Build Settings 窗口,选择 Android 平台,点击 "Player Settings" 按钮,进入 Player Settings 界面。在 Android Settings 标签页中,找到 "Publishing Settings" 部分,检查 "Custom Gradle Template" 是否包含正确的依赖项。如果缺少某些依赖项,可以手动添加到 Gradle 文件中。然后重新构建 APK。 4. 检查 Android SDK 和 JDK 版本 在 Unity 中打开 Build Settings 窗口,选择 Android 平台,点击 "Player Settings" 按钮,进入 Player Settings 界面。在 Android Settings 标签页中,找到 "Publishing Settings" 部分,检查 "Minimum API Level" 和 "Target API Level" 是否正确。然后检查是否安装了正确版本的 Android SDK 和 JDK。如果没有安装,可以手动下载并安装。然后重新构建 APK。 5. 检查 AndroidManifest.xml 文件 在 Android 项目中,检查 AndroidManifest.xml 文件是否包含正确的权限和组件声明。如果缺少某些声明,可以手动添加到 AndroidManifest.xml 文件中。然后重新构建 APK。 希望以上方法能够帮到您解决问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值