请选择 进入手机版 | 继续访问电脑版

查看: 1745|回复: 0

[技术] 详解Animation C# Jobs

[复制链接]

1192

主题

1893

帖子

2万

贡献

管理员

Rank: 9Rank: 9Rank: 9

积分
24845
QQ
发表于 2018-8-29 19:04:31 | 显示全部楼层 |阅读模式
在Unity 2018.2中,Animation C# Jobs通过使用在Unity 2018.1中推出的C# Job System扩展了动画Playables。在实现动画系统时,Animation C# Jobs让开发者能自由创建原始解决方案,同时使用安全的多线程代码来提高性能。

Animation C# Jobs是一个底层API,使用它需要对Playable API有充分的了解。使用Animation C# Jobs,你可以编写在PlayableGraph中用户定义位置调用的C#代码。用户自定义的C#脚本可以修改经过PlayableGraph的动画流。

Animation C# Jobs针对有兴趣将Unity动画系统扩展更多功能的开发人员,使用它可以充分利用现代多核硬件的强大功能。对于在主线程上C#脚本性能消耗较大的项目,可以并行化一些动画任务,让性能得到宝贵的提升。

功能
Animation C# Jobs拥有以下功能:
  • 全新Playable节点:AnimationScriptPlayable
  • 控制PlayableGraph中的动画数据流
  • 多线程C#代码

特别提示:
Animation C# Jobs目前仍是一个实验性功能,具体API在UnityEngine.Experimental.Animations。该API之后会根据用户反馈改进。

适用场景
Animation C# Jobs适用以下示例场景:

例如:你想给全新的龙角色加入一个足部锁定功能。可以使用常规MonoBehaviour对其进行编码,但在动画播放结束之前所有代码都将在主线程中运行。如果使用Animation C# Jobs,你可以编写算法并将直接在PlayableGraph中的自定义Playable节点中使用它,代码将于PlayableGraph处理期间在独立线程中运行。

你还可以不用特地设置龙尾部的动画,通过Animation C# Jobs将能完美设置程序化计算动作的功能。Animation C# Jobs还能让你编写具体的LookAt算法。例如:帮助定位龙颈部的10块骨骼。

另一个很好的示例是制作自定义的动画混合器。假设你有一些具体需要的内容:该节点会从一次输入中获取位置,从另一次输入获取旋转角度,从第三个节点获取缩放值,然后将它们混合为单个动画流。Animation C# Jobs能帮助你实现创意的功能,并根据特定需求进行构建。

获取示例
在深入了解如何使用Animation C# Jobs API之前,让我们参考一些示例,它们将展示了使用Animation C# Jobs可以实现什么。

所有示例都可以访问Animation Jobs Samples的GitHub页面获取。安装完成后,示例中自带场景存放在Scenes目录下。

下载地址:https://github.com/Unity-Technologies/animation-jobs-samples

01.png


示例解析
下面我们将为大家一一解析这些示例。

1、LookAt
LookAt是一个非常简单的示例,它将骨骼朝向一个效应器。如下图所示,你可以了解它如何应用于3D Game Kit资源包中的角色对象。
02.gif


2、TwoBoneIK
TwoBoneIK实现了一个简单的双骨骼IK算法,它能应用于三个连续关节。例如:人类手臂或腿。演示中的角色使用通用Humanoid Avatar制作。
03.gif


3、FullbodyIK
FullbodyIK展示如何修改Humanoid Avatar上的数值。例如:目标、提示、观察对象和身体旋转等。下图示例使用了动画流的人形实现。
04.gif


4、Damping
Damping展示了实现可应用于动物尾巴或人类马尾辫的阻尼算法,说明了如何生成程序化动画。
05.gif


5、SimpleMixer
SimpleMixer是一个非常基础的动画混合器。它接收二个输入流,然后将它们基于混合数值结合起来,功能类似AnimationMixerPlayable 。
06.gif


6、WeightedMaskMixer
WeigthedMaskMixer示例是一个较高级的动画混合器。它接收二个输入流,然后将它们基于权重遮罩混合起来,该权重遮罩会定义如何混合各个关节。例如:你可以播放经典闲置动画,然后从另一动画剪辑接收手臂动画。或是通过在脊骨上连续应用较高的权重来平滑上身动画的混合效果。
07.gif


API
Animation C# Jobs由Playable API提供支持。它带来了三个新结构:AnimationScriptPlayable、IAnimationJob和AnimationStream。

AnimationScriptPlayable和IAnimationJob
AnimationScriptPlayable是一个全新的动画Playable,和其它Playable一样,它可以添加到PlayableGraph的任何位置。它包含一个动画作业,能够作为PlayableGraph和作业之间的代理。该作业是个实现IAnimationJob的用户定义结构。

Playable的常见作业流程会输入动画流并混合流中的结果。该动画流程分为二个通道,每个通道都自带IPlayableJob中的回调函数:
  • ProcessRootMotion会处理Root Transform的动作,它会在ProcessAnimation前调用,并根据Animator剔除模式确定是否调用ProcessAnimation。
  • ProcessAnimation用于除Root Motion根动画外的所有对象。

下面示例是一个非常基础的Animation C# Jobs。它会帮助我们了解如何使用动画作业创建AnimationScriptPlayable。
[C#] 纯文本查看 复制代码
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;
using UnityEngine.Experimental.Animations;

public struct AnimationJob : IAnimationJob
{
    public void ProcessRootMotion(AnimationStream stream)
    {
    }

    public void ProcessAnimation(AnimationStream stream)
    {
    }
}

[RequireComponent(typeof(Animator))]
public class AnimationScriptExample : MonoBehaviour
{
    PlayableGraph m_Graph;
    AnimationScriptPlayable m_ScriptPlayable;

    void OnEnable()
    {
        // 创建视图
        m_Graph = PlayableGraph.Create("AnimationScriptExample"); 

        // 创建Animation job和Playable
        var animationJob = new AnimationJob();
        m_ScriptPlayable = AnimationScriptPlayable.Create(m_Graph, animationJob);

        // 创建输出结果并将结果链接到Playable上
        var output = AnimationPlayableOutput.Create(m_Graph, "Output", GetComponent<Animator>());
        output.SetSourcePlayable(m_ScriptPlayable);
    }

    void OnDisable()
    {
        m_Graph.Destroy();
    }
}

作为IAnimationJob方法的参数传递的流,它是在每个处理通道中将要处理的对象。

默认情况下,所有AnimationScriptPlayable输入会被处理。在仅有一个输入即后期处理作业的情况下,该流会包含已处理输入的结果。在有多个输入即混合作业的情况下,最好是手动处理输入。

为了实现手动处理,AnimationScriptPlayable.SetProcessInputs(bool)将启用或禁用输入的处理通道。为了触发输入的处理过程并获取手动模式中的结果流,请调用AnimationStream.GetInputStream()。

AnimationStream和句柄
通过AnimationStream,开发者可以访问视图中Playable之间流动的数据。它还可以访问由Animator组件设置动画的所有数值。
[C#] 纯文本查看 复制代码
public struct AnimationStream
{
     public bool isValid { get ; }
     public float deltaTime { get ; }

     public Vector3 velocity { get ; set ; }
     public Vector3 angularVelocity { get ; set ; }

     public Vector3 rootMotionPosition { get ; }
     public Quaternion rootMotionRotation { get ; }

     public bool isHumanStream { get ; }
     public AnimationHumanStream AsHuman ( ) ;

     public int inputStreamCount { get ; }
     public AnimationStream GetInputStream ( int index ) ;
}

由于同样的数据可能在流的帧与帧之间处于不同的偏移,所以无法直接访问流数据。例如:通过在视图中添加或移除AnimationClip,该数据或许已经移动或是不存在于流中。

为了确保这些访问的安全性和有效性,我们引入了二组句柄:流句柄和场景句柄,其中每个句柄都带有变换和组件属性句柄。

1、流句柄
流句柄(Stream Handle)会安全地管理所有对AnimationStream数据的所有访问。如果发生错误,该系统会抛出C#异常。流句柄共有二个类型:TransformStreamHandle和PropertyStreamHandle。

TransformStreamHandle管理Transform并处理变换层级。这意味着你可以修改流中的本地或全局Transform位置,并且为以后位置的请求将提供可预测的结果。

PropertyStreamHandle管理系统可以在其它组件上设置动画和查找的所有其它属性。例如:它可以用于读取或写入Light.m_Intensity属性的数值。

[C#] 纯文本查看 复制代码
public struct TransformStreamHandle
{
    public bool IsValid(AnimationStream stream);
    public bool IsResolved(AnimationStream stream);
    public void Resolve(AnimationStream stream);

    public void SetLocalPosition(AnimationStream stream, Vector3 position);
    public Vector3 GetLocalPosition(AnimationStream stream); 

    public void SetLocalRotation(AnimationStream stream, Quaternion rotation);
    public Quaternion GetLocalRotation(AnimationStream stream);

    public void SetLocalScale(AnimationStream stream, Vector3 scale);
    public Vector3 GetLocalScale(AnimationStream stream);

    public void SetPosition(AnimationStream stream, Vector3 position);
    public Vector3 GetPosition(AnimationStream stream);

    public void SetRotation(AnimationStream stream, Quaternion rotation);
    public Quaternion GetRotation(AnimationStream stream);
}

public struct PropertyStreamHandle
{
    public bool IsValid(AnimationStream stream);
    public bool IsResolved(AnimationStream stream);
    public void Resolve(AnimationStream stream);

    public void SetFloat(AnimationStream stream, float value);
    public float GetFloat(AnimationStream stream);

    public void SetInt(AnimationStream stream, int value);
    public int GetInt(AnimationStream stream);

    public void SetBool(AnimationStream stream, bool value);
    public bool GetBool(AnimationStream stream);
}

2、场景句柄
场景句柄(Scene Handle)是安全访问任意数值的另一种形式,但是它来自场景而不是AnimationStream。对于流句柄而言,有二种场景句柄:TransformSceneHandle和PropertySceneHandle。

场景句柄的一个具体用法是对Foot IK实现效应器(Effector)。IK效应器通常是未经过Animator设置动画的游戏对象,因此它在Transform外部由PlayableGraph中的动画剪辑修改。该作业需要获取IK效应器的全局位置,从而计算足部的合适位置。因此IK效应器要通过场景句柄访问,同时流句柄将用于处理腿部骨骼。
[C#] 纯文本查看 复制代码
public struct TransformSceneHandle
{
    public bool IsValid(AnimationStream stream);
 
    public void SetLocalPosition(AnimationStream stream, Vector3 position);
    public Vector3 GetLocalPosition(AnimationStream stream);

    public void SetLocalRotation(AnimationStream stream, Quaternion rotation);
    public Quaternion GetLocalRotation(AnimationStream stream);

    public void SetLocalScale(AnimationStream stream, Vector3 scale);
    public Vector3 GetLocalScale(AnimationStream stream);

    public void SetPosition(AnimationStream stream, Vector3 position);
    public Vector3 GetPosition(AnimationStream stream);

    public void SetRotation(AnimationStream stream, Quaternion rotation);
    public Quaternion GetRotation(AnimationStream stream);
}

public struct PropertySceneHandle
{
    public bool IsValid(AnimationStream stream);
    public bool IsResolved(AnimationStream stream);
    public void Resolve(AnimationStream stream);

    public void SetFloat(AnimationStream stream, float value);
    public float GetFloat(AnimationStream stream);

    public void SetInt(AnimationStream stream, int value);
    public int GetInt(AnimationStream stream);

    public void SetBool(AnimationStream stream, bool value);
    public bool GetBool(AnimationStream stream);
}

AnimationJobExtensions
最后一部分是AnimationJobExtension类,它是作用所有功能的“粘合器”。它使Animator得到扩展,从而创建出上述的句柄。

AnimationJobExtension共有四个方法:BindStreamTransform、BindStreamProperty、BindSceneTransform和BindSceneProperty。

[C#] 纯文本查看 复制代码
public static class AnimatorJobExtensions
{
    public static TransformStreamHandle BindStreamTransform(this Animator animator, Transform transform);
    public static PropertyStreamHandle BindStreamProperty(this Animator animator, Transform transform, Type type, string property);

    public static TransformSceneHandle BindSceneTransform(this Animator animator, Transform transform);
    public static PropertySceneHandle BindSceneProperty(this Animator animator, Transform transform, Type type, string property);
}

BindStream方法可用于在已设置动画的属性上创建句柄,或是为流中新设置动画的属性创建句柄。

参考资料
下面是一些参考的API文档,请访问Unity在线手册,输入相应关键字进行查看。
  • AnimationScriptPlayable
  • IAnimationJob
  • AnimationScript
  • TransformStreamHandle
  • PropertyStreamHandle
  • TransformSceneHandle
  • PropertySceneHandle


小结
关于Animation C# Jobs就介绍到这里了,如果你遇到错误,请使用Unity中的Bug Reporter来报告错误。

有关此实验性功能的任何反馈,请访问Unity官方英文论坛帖子提出:
https://forum.unity.com/threads/ ... in-2018-2a5.525012/

更多Unity的技术资料尽在Unity官方中文论坛(UnityChina.cn)!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表