详解Animation C# Jobs
在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)!
推荐阅读
活动信息
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官方技术论坛