查看原文
其他

详解Animation C# Jobs

Unity Unity官方平台 2018-11-15

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


示例解析

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


1

LookAt

  

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



2

TwoBoneIK 

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



3

FullbodyIK 

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



4

Damping 

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



5

SimpleMixer 

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



6

WeightedMaskMixer 

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


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。

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组件设置动画的所有数值。

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属性的数值。


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效应器要通过场景句柄访问,同时流句柄将用于处理腿部骨骼。


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。


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/animation-c-jobs-in-2018-2a5.525012/

 

更多Unity的技术资料尽在Unity官方中文论坛(UnityChina.cn)!


推荐阅读


活动信息

Unity视觉艺术专题论坛报名开启

9月11日,Unity将参与由中央美术学院、中国文化娱乐行业协会联合举办首届游戏艺术大展,并举办Unity视觉艺术专题论坛,邀请所有对技术与艺术游戏及视觉艺术感兴趣的人员参与本次活动。[了解详情...


8月Asset Store资源商店促销 

8月期间,只需在Asset Store资源商店消费满50-255美元,即可获赠2-6款精品资源,最高消费250美元,更可附赠获得一件Asset Store定制款T恤!

活动地址:

https://assetstore.unity.com/g/august-promo-activation



点击“阅读原文”访问Unity官方技术论坛

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存