DMotion - 基于DOTS的动画框架和状态机

DMotion - 基于DOTS的动画框架和状态机

【博物纳新】专栏是UWA旨在为开发者推荐新颖、易用、有趣的开源项目,帮助大家在项目研发之余发现世界上的热门项目、前沿技术或者令人惊叹的视觉效果,并探索将其应用到自己项目的可行性。很多时候,我们并不知道自己想要什么,直到某一天我们遇到了它。

今天推荐的项目来自UWA开源库:
https://lab.uwa4d.com/Lab/62bbf82fa8103dabd0f87c8b

一、简介

自从Unity推出了DOTS(Data-Oriented Technology Stack,面向数据的技术堆栈)之后,关于其应用一直备受关注。虽然DOTS对性能的提升比较明显,但是由于其不算低的门槛,仍然让许多开发者难以上手。以动画为例,截至目前的Entities 1.0版本仍然没有官方的基于Entities的动画解决方案。本文介绍的插件DMotion就提供了基于DOTS的动画框架和状态机,可以帮助开发者更便捷地使用DOTS制作动画

二、功能概览

DMotion的基础是另一个开源项目Latios-Framework的Kinemation部分。这个项目使用DOTS建立了一些常用模块的框架,包括物理、音频、动画等等,目前仍在频繁更新中,对于希望学习和使用DOTS的开发者也可以作为参考,链接如下:
https://lab.uwa4d.com/Lab/5dffdd368bab6aaf026a65c3

由于Latios-Framework使用了另一种动画压缩方案,即使用了插件Animation Compression Library,这个插件虽然理论上可以用于移动端,但是目前还没有进行相应的适配,因此DMotion目前只能在PC平台上使用。作者提到之后会考虑扩展到移动端上。

目前DMotion的主要功能如下:
Current Features (V0.3.4):

  • Fully Bursted Runtime
  • State Machine Visual Editor
  • Transitions: Boolean, Int, Enum And End Time
  • Simple API For Playing Clips Through Code (See Samples)
  • 1D Blend Tree
  • Animation Events
  • Root Motion (With Writegroup Support, If You Need To Override Default Behaviour)
  • Object Attachment
  • Support For Optimized And Non-Optimized Skeletons
  • State Machine Visual Debugging

之后可能会更新的功能:
Planned Features:

  • 2D Blend Tree (Cartesian/Freeform)
  • State Machine Override (A.K.A Animator Override Controller)
  • Substates
  • IK Support
  • Multiple Layers
  • Skeleton Masks

三、使用

Latios-Framework最新的版本(目前为0.6.4)已经支持Entities 1.0,但是DMotion还只支持Entities 0.51版本,因此安装DMotion的Package之后需要安装Latios-Framework的旧版本,建议使用0.5.8版本。

DMotion这个插件的使用整体上是比较简单的,可视化程度高,作者提供了Sample和详细的指导,需要使用代码的地方都有例子可以参考。文档链接如下:
https://github.com/gamedev-pro/dmotion/wiki/1.1-Getting-Started:-The-Basics

这里只做一些简单的介绍,比如最简单地播放一个Animation Clip。

首先需要创建一个DMotion的Clip。

然后在Inspector界面中设置好要用到的Animation Clip。

再给需要播放动画的对象添加对应的组件,在组件中做一些设置,添加需要使用的Clip,就可以播放动画了。

点击Play即可播放动画。

动画状态机部分可视化程度很高,和Unity本身的动画状态机的使用方法很接近。

首先创建一个动画状态机。

创建一个新的状态。

添加对应的Animation Clip。

两个动画状态之间可以建立Transition。

Transition可以设置一些参数和条件。

这些内容都可以在文档中找到,因此不再赘述。

四、实现原理

DMotion是一个基于DOTS的框架,这里我们从播放动画的代码开始分析。

继承IConvertGameObjectToEntity是为了调用Convert函数把GameObject转化为Entity 。继承IRequestBlobAssets是为了调用RequestBlobAssets,把Animation Clip转化成DOTS支持的格式,这里使用了插件Latios-Framework中的Kinemation部分,最终会转化成一种BlobAsset,这是DOTS当中针对Streaming做了优化的二进制数据结构。

Convert函数的具体内容,把GameObject转化为Entity并添加对应的Component。

使用RequestBlobAssets把Animation Clip转化成BlobAsset的部分。在运行的时候,DMotion使用的代码都是使用Burst编译的。在System文件中的代码是运行时对Entities的Component进行处理的部分,也就是ECS中的“S”,可以看到这些代码都是使用Burst编译的,并且使用了Job System。

可见DMotion充分利用了DOTS系统,可以预测到其性能表现应该是比较优秀的。

五、性能

首先介绍一下DOTS为什么会比传统方式更快。Unity推出的DOTS主要包括三个方面,分别是ECS、Burst Complier和Job System。Entities是游戏中的事物,或者说是一些数据的集合。Components把与Entity相关的数据组织起来,Systems则是把Components的数据从当前状态转换为下一个状态的逻辑。下图展示了ECS的组织架构。

ECS架构在执行逻辑时,只会操作需要操作的数据。System在操作数据的时候只会收集它关心的Component数据,CPU运行时就会将这一整块内存装入高速缓存中,这样就减少了Cache Miss次数,增加了缓存命中率,整体上提高了程序效率。此外现代CPU中使用的SIMD技术与这种数据密集的架构相性极好,可以进一步提高性能。

ECS模式更加适合现代CPU架构,因为它可以做到高效处理数据,而不用把多余的数据字段存入宝贵的缓存从而导致多次Cache Miss。比如操作Unity对象的Position属性,会把GameObject所有相关数据都加入缓存,浪费了宝贵的缓存空间。而如果在ECS架构下,将只会把Position属性放入内存,节省了缓存空间,也一定程度上减少了Cache Miss。

这种数据结构很适合并行处理。Burst Complier是使用LLVM从IL/.NET字节码转换为高度优化的本机代码的编译器,与Job System一起生成多线程并行处理的代码,充分利用SIMD,多线程操作充分发挥ECS的优势。因此Unity的DOTS往往比传统方式速度更快。

由于DOTS本身的特性,DMotion的性能表现比Animator更为优秀。在文档中作者认为性能提升可以达到大约6倍左右,而我们的实际测试结果为大约快3倍左右。

测试环境:
平台:Windows 10 (10.0.19044) 64bit
Unity版本:2021.3.9f1c1
GPU:Intel(R) UHD Graphics 750/Direct3D 11.0
CPU:3.6ghz/11th Gen Intel(R) Core(TM) I7-11700K @ 3.60ghz
测试工具:UWA GOT Online

测试用例:
使用的Model如下,拥有5770个顶点和29个骨骼。

使用的动画是行走的动画,分别使用DMotion和Animator进行测试。

测试内容:
加载并播放了2500个带有动画的模型,测试帧率。

测试结果如下:
DMotion:
平均帧率为15.33帧

Animator:
平均帧率为5.5帧

可见在性能上DMotion相比Animator有一定提升,可惜的是目前还不适配移动平台。感兴趣的同学可以关注这个项目的更新情况,也可以尝试在其基础上做进一步的改进。