应广大粉丝的愿望,首届“团结大嘉宾”线上分享会文字版来了。 本文节选两位嘉宾分享的部分内容。 如果您想观看完整版以及直播问答环节的精彩内容,请前往B站。
B站观看地址:
《永恒》的动作和动作系统
大家好,我是24娱乐工作室的姚锦衣。 今天给大家带来的是《永劫》的动作和动作系统。
首先,我们简单介绍一下我们的游戏以及我们如何使用Unity引擎来开发它。 我们的《永恒之劫》是一款多人动作电脑游戏。 在整个开发过程中,我们对引擎做了很多定制开发。 Unity官方的技术支持部门和一些常驻工程师在开发过程中也给了我们很多帮助,特别是帮助解决了一些疑难问题。 问题。
今天我主要分享一下我对动作和运动系统的经验和见解。
01动作系统概述
《永劫》的动作系统主要采用了Unity中的人形骨骼系统,也就是Humanoid的动作。 我们目前有两种英雄体型和少量非英雄体型怪物。 动作状态数量约为2000个,目前有超过9000个动作片段。 正式推出的数量可能会超过 10,000 个,因为我们还在继续制作新的英雄和武器。
我们的动作既是动作捕捉的,又是手工制作的。 动作捕捉多用于一些角色的攻击和死亡的动画表现。 因为这部分的动作,我们希望尽可能的真实、丰富、流畅。
我们还经常使用分层动画系统。 就像我们的英雄目前有10层,可以说很多,包括基础层、左右手、头部左右手、上半身、手指层等。比较常见的包括跑步与下半身并瞄准上半身进行射击。
当我们研究动画系统时,我们通常会讨论一个主题,即如何控制角色位移。 主流的方法有两种,一种是使用动作本身来启动,即开启RootMotion; 另一种是使用程序计算角色的位移并应用其位移。 总体而言,RootMotion 在我们的游戏中大部分情况下都使用。
首先,我们的动作美术师在动作游戏开发方面有着非常丰富的经验。 它们可以让一个角色的位移,比如简单的奔跑,更有节奏感和真实感,所以我们在这部分位移方面拥有主动权。 这一切都献给了艺术。 程序位移也有少量应用。 例如,跳跃需要根据角色跳跃的长度来跳跃不同的高度,包括可能会以不同速度和方向飞行的飞绳。 有些被击中时的位移需要配置不同的参数来表示不同距离的位移、不同时间长度的刚度等。 这部分的位移是由程序驱动的。
02Playable API构建底层动作控制系统
我们知道,在Unity中的Animator中构建动作状态机是非常方便的。 你可以直接在Animator Controller中创建一些动作状态,添加一些过渡动作过渡连接,设置一些参数和过渡条件,配置好每个状态对应的动画,基本上就是一个可以运行的动作状态了。
然而,在像我们这样非常注重动作的游戏项目中应用这种方法还是存在一些困难的。 一个非常典型的问题是我们前面提到我们有超过2000个动作状态。 如果我们将超过 2000 个节点放入 Animator Controller 中,则几乎无法维护。 幸运的是,Unity仍然提供了Playable API,允许我们自定义重写运动控制系统。 我们的项目还使用这个 Playable API 来重建一组底层动作控制系统。 今天我们主要讲一下使用这样的机制的好处。
首先,它可以控制动画加载策略,可以按需加载,也可以异步加载。 第二点是Playable Gragh的数据流可以更灵活地控制,并且可以很容易地插入一些自定义的AnimationJobs来创建一些特殊的动作机制和动作表演。 第三点是我们可以加载定制的配置数据,这样动作系统可以更容易地与其他游戏系统集成。 最后,我们可以创建一个自由度更高的Override机制。 我们知道Animator本身就有Override Animator的概念。 在我们的项目中,我们其实做了一个更高级的Override机制,可以让一些动作更方便的组合和覆盖。
这里有一些参考资料,基本上都是使用Playable API重新构建了动画控制系统。
[1] 简单动画
[2] 动画师:
03ProBulider工作流程
现在我们来分享一下如何进行离线标注。 这里不得不提一个很棒的Unity工具ProBuilder,它在我们的项目中发挥了非常大的作用。 大致有三个方面:一是关卡原型; 二是测试场景的搭建; 三是辅助特殊动作触发区域的设置。
首先我们来说一下如何使用ProBuilder来设计关卡原型? 我们项目中一个场景的构建过程大致分为三个步骤。 首先是关卡规划,构建场景白色模型。 二是通过迭代不断验证玩法。 这个场景白色模型可以符合我们的游戏玩法。 当整个场景的拓扑结构大致确定之后,我们的美术最终就会进来对场景进行美化。
这个工作流程也是当前游戏开发尤其是3D游戏开发中比较规范、科学的开发流程。
这里场景的白色模型构建基本上是使用ProBuilder来实现的。
ProBuilder的第二个功能是构建一些测试区域。 对于我们所有的运动触发区域,比如障碍物游戏素材,我们的规划人员都会在测试场景中搭建不同规格的障碍物。 无论是程序开发还是QA测试,都会提供很大的便利。
第三个是我们前面提到的运动触发区域的离线注释系统。 我们还通过 ProBuilder 插件为此提供一些帮助。
比如在爬树触发区域,我们可以选择一些树面,刷出触发。 对于屋檐的触发区域,可以选择屋檐的线条,刷出触发。 比如这个天花板或者这个索梁,你可以选择相应的表面来刷出触发。
总的来说,我们可以使用ProBuilder更方便地选择模型中的点、线、面,并使用一些操作直接刷出触发器,这比手动放置一些触发器对齐场景效率更高。
姚金一老师还分享了《永劫》运行时如何检测周围环境、如何管理物理数据、动作录制和回放工具、离线动作采样工具等,完整版已上传至B站。
Animator定制优化方案
感谢刚才《永劫》的朋友给我们带来了非常精彩的分享。 今天我们就挑个话题来说说一个Animator定制方案。
今天我们关注一个热点问题——Animator 中的 Override。 首先我们从原理角度来分析一下,了解一下这个热点是如何形成的。
现在我的演示中有两个 U 酱。 这两个U酱现在处于Tpose状态,分别称为Unity Normal和Unity Mass。 他们是相同的头像,但我给了他们每个人不同的控制器。
Normal 的 Unity 酱添加了一个具有两个状态级别的标准 Unity 控制器。 现在我们在状态层有了一个名为test的新状态,它对应于我自己制作的一个空动画; 另一层是脸。 该图层原本是用来控制demo中小姐姐的动作和表情的。 状态。
我们再看一下弥撒。 Mass上面的层就是我们的基础层,和刚才的Normal是一模一样的。 我们还在此处添加了状态测试,并在此处添加了半测试。 我们在面层做了很多状态。 其实我们只要复制粘贴上面的状态就可以了。
可以看到我们屏幕上有两个按钮,一个叫Override Normalunity 动画自带位移,另一个叫Override Mass。这两个操作几乎做的是同一件事情,就是我们给它同样的Override,然后等待虚拟节点在控制器中。 动画进去了。
当我进入 Override Normal 和 Override Mass 时,在 Override 上花费的时间在 Mass 中会更多。
为什么有这么多弥撒?
在Unity的Override中,我们发现会有很长一段时间。 这段时间你在做什么? 这是一件非常神奇的事情,因为Unity在Tag中看不到更详细的信息。 事实上,这里Unity会尝试将你的Animator Controller中的所有状态合并到一个称为Animation set的数据结构中,并且它会将所有数据结构放入其中。 所有数据意味着您体内的所有动画剪辑。 请注意,将所有剪辑使用的曲线相乘需要一系列操作。
什么意思? 我们可以看一下这条曲线,并在其中找到一个动画剪辑。 当我们点击这个的时候,就会出现一些统计曲线数字。 这些曲线数字对于优化是有意义的。 在此您将看到一条称为 const 的曲线。 如果const曲线的比例越高,在进行我们刚才提到的计算时,所需的计算量就会相对较少。
上面有很多曲线,比如curvepose、Euler、scale等等,这些曲线需要经过一系列的合并操作,也就是说后面你采样的时候这些曲线才会真正参与到计算中。 const 曲线是一个数字,您可以将其视为以常数的形式参与。 因此const曲线带来的性能消耗并不大。 对于我这样的人来说,100%没问题。 但是在实际项目中,我们会看到有很多曲线,大概有20%到10%左右,剩下的Stream曲线会占到非常非常高的比例。
另外,我们还将查看这些行的总数。 比如我这里有10个AnimationClip,每个AnimationClip大约有300条曲线。 你可以简单地认为它需要进行3000次操作。 这些操作当然不是线性比例,但大致是这样的线性关系。
也就是说,在我们的状态下3D场景,你的基本状态越多,每个状态所使用的曲线就越复杂。 不管这个节点最终是否参与你runtime的最终性能,都会在Override时造成性能消耗。 的。
当然,如果我们在实际工作中使用Animator系统,它的总体数量可能会比我的demo中的数量大,甚至大几个数量级。 包括一些宿主项目和其他较大的项目,如果使用Animator,也会面临同样的问题。
我们来看看如何优化?
优化也很简单。 第一种方式是尽可能降低整个状态机的复杂度。 比如我们可以在里面尽可能的使用dummy。 例如,如果我们想在这个地方重写相同的状态,我们应该在基本状态机中使用尽可能少的复杂动画。 不要从一开始就将它们全部放入基本状态机中。 如果你的基本状态机非常复杂,例如有数千个,你一次最多可以覆盖其中两个或三个。 这种性能比较差,因为每次都要把这些不可移动的物体都计算一次。
所以在这种情况下,我们建议大家将其拆分为多个Controller来控制,或者有的开发者将Animator拆分为两个或三个Animator来控制unity 动画自带位移,这也是可以的。 总而言之,就是减少你的基本状态机中Animation的大小或者动画的数量。
另外一个,我们在制作动画的时候,在检查一个动画的时候,我们应该关注曲线的数量,尽量增加它的const曲线数量。 有同学说我导入的时候不是const。 我应该怎么办? 导入到 Unity 时实际上有导入选项。 如果你做过一些一般性的优化,你都知道我们的动画压缩中有一个关键帧减少,或者说是可选的。 一般来说,以上选项是比较直接的。
另一种情况如何优化? 比如我们这里有一个Keyframe,我们会发现它有一个偏差值,但是这个偏差值不是一个绝对值,而是一个比例。 例如,0.5,当我K-out动画本身时,运动范围非常非常小。 比如上一帧是0.000000...1,下一帧又多了一个0和1,那么它的比例其实是比较大的。 但实际上,肉眼看来,这东西几乎没有任何变化。
因此,你经常会看到一些信息说我们可以尝试降低FBX中动画的精度。 例如,我们只保留小数点后5或3位的动作,这样我们的关键帧缩减可以压缩更多已经接近的关键帧,从而减少曲线数量。 这可以减少一些内存,因为你所有的曲线关键帧数据最终都会在Animationset过程中被填充到一个内存块中,这本身就比较昂贵。 此外,它还可以生成更多 const 动画曲线,从而减少您的整体 Override 消耗。
当然,还有另一种方式可以像我们《天长地久》里的朋友一样使用时间线。 因为Timeline和Animator最大的区别就是Animator在设计上是把整个Controller当成一个整体。 什么意思? 从运行时的角度来看,我以后所有的操作都是一个整体的Controller,而不是你图中看到的A动画和B动画。 从Unity编辑器的角度来看,完成这些合并之后,只剩下一个Animator Controller,其中包含了大量的数据。 因此,任何Override、任何对Controller的修改都是对整体数据集的修改,而且这个修改是非常巨大的。
Unity在制定时间线时避免了这个问题。 时间线实际上是根据您的每个剪辑进行修改的。 当我覆盖剪辑或替换剪辑时,实际需要更新的唯一数据是剪辑。 因此,它的整体性能消耗会比较平缓,不会像Animator那样容易出现峰值。
最后,我想推荐一个我为开发者定制的解决方案。 这个优化方案的优化思路是什么?
由于很多项目团队中需要重写的动画都是dummy,而且很多开发团队在实际开发过程中经常会改变它,所以他们会在自己的基本状态机中放入一个demi。 既然是这样,那不如先做一个前提:约定你要Override的动画必须是这个状态机中的空动画。 基于此,我们可以玩出很多花样。
优化后效果如何? 我之前测的比较稳定的数字是1.32左右,也就是说我们基于Unity优化之后,从4:00到了1.32。
今天我要说的是动画。 最近我为不同的游戏厂商制作这样的定制解决方案已经有一年左右的时间了。 如果您有兴趣,还可以前往我们的官方网站咨询更多优质低价的定制解决方案。 它花费不多,但您可以拥有一个完全属于您的 Unity 引擎,听起来很酷。 您可以扫描此二维码或通过邮件联系我们,咨询相关优化方案。
咨询邮箱:
好了,今天的分享就到这里了。 谢谢你们。
以上是首届“团结大咖嘉宾”线上分享会的精彩内容。 下一场将于5月28日举行,据说受邀嘉宾来自《鬼泣-巅峰斗士》,敬请期待。