N.GAME丨构建公平的联机环境,服务器动画性能优化

2022N.GAME网易游戏开发者峰会于「4月18日-4月21日」举办,本届峰会围绕全新主题“未来已来 The Future is Now”,共设置创意趋势场、技术驱动场、艺术打磨场以及价值探索场四个场次,邀请了20位海内外重磅嘉宾共享行业研发经验、前沿研究成果和未来发展趋势。

今天的干货来自技术驱动场的嘉宾许飞,他是网易互娱引擎部技术专家。

以下是嘉宾分享实录:(部分删减与调整)

大家好,我是来自网易互娱技术中心的许飞。

今天我的分享内容主要有四个部分:服务器性能优化的意义和现状;我们将会探讨一下看看业界常见优化的方法;我们会着重来讲一下我们在复杂的动画状态机优化方面的一些做法;展望一下服务器动画一些发展和未来。

一、服务器动画的意义与现状

首先进入第一部分。很多有经验的开发者可能会有疑问,服务器需要跑动画吗?这是一个好问题,因为现在大多数游戏其实服务器是没有跑动画的。

在传统观点中,动画和渲染特效一样。它是属于表现层次,只要客户端看就可以了。

有限的几种需要动画参与的逻辑,比如打击部位的判定,服务器不跑但客户端还是有动画。我们让客户端来进行判断,然后把结果发送给服务端,也能实现一样的效果。这也是我们以前常见游戏的做法。

需要提出的是,这一方法需要建立在网络必须是可信任的前提下,但实际的网络环境并非如此。

根据Peter Steiner在1993年发布于纽约客上的一副图画,它被认为揭示了互联网环境的复杂性。互联网环境是缤纷多彩的,这给网络游戏的发展提供了肥沃的土壤。与此同时,互联网的复杂性也给游戏开发者造成了很大的挑战。

例如在游戏外挂方面。在竞技类游戏中,如果玩家一旦使用外挂,就会对游戏的公平性造成毁灭性打击。那么有没有一种办法能够有效地防止或者说反外挂呢?答案是有的。一个有效的方法叫做服务器权威。

简单介绍下其中思路。外挂是通过劫持游戏客户端来实现一些非法操作,相对于玩家客户端来讲,我们的服务器是在经过严密保护的机房里,一般的外挂开发者很难劫持我们的服务器。

如果我们把关键的逻辑都放在服务器上面,客户端仅仅作为一个指令的输入者,我们就可以防止大部分外挂的操作。

那为什么现在大多数游戏没有选择这种做法?这就需要考虑到很多现实问题,例如在游戏各种不同系统开销层面,包括数据量的大小、更新频率的高低等。

我们可以看到,很早期的服务器其实只会保存等级之类的信息。发展了一段时间之后,服务器就可以保存装备、技能等信息。而在动画方面,它需要的数据量和更新频率都很庞大,这对算力的要求十分高。

简单来说,如果我们简单的把动画从客户端挪到服务器,在没有优化的情况下会导致服务器直接跑不起来。基于这样尴尬的现实,很多游戏都没有在服务器开启动画。

那随着技术的发展,是否存在让服务器搭载动画变成可能呢?我们稍微梳理下过往的产品历史。

我们收集了一段时间来服务器每条线程的成本,其中2007年、2016年和2020年的比较典型,同时这三年也诞生出三款典型的射击游戏。

从2007年到2016年,我们服务器单线程的成本大概降到了原来的1/3,2016年到2020年的成本更是降到了原来的1/2。2007年《穿越火线》发布,它的服务器几乎没有跑任何动画相关的东西,2016年的《守望先锋》跑了一部分。

而2020年发布的《瓦罗兰特》,它的服务器是完全跑动画的。什么意思呢?它会完全计算角色在服务器上的状态。此前,该款游戏的的主程在分享中也明确说到,他们这样做就是为了反外挂。

因为服务器只有有了非常全面的动画信息,我们在判定受击的时候,才不至于被客户端的外挂所欺骗。

我们也相信,随着技术发展,服务器动画的逻辑执行程度会越来越高。

二、服务器动画常见优化方向与方案

既然要在服务器跑动画,就需要优化它的动画开销。

而动画系统开销组成部分包括:条件更新、状态更新和姿态更新。

对于一个动画系统来讲,它其实可以理解成这个系统接收外界的输入,然后更新内部状态,最后计算出模型的姿态。

首先是输入部分,一般为角色速度或者角色状态,当前是释放技能还是做其它也好。动画状态,比如说角色的速度变化,可能从一个静止状态变成一个跑动状态,或者说从跑到跳的状态变化,最后再由这些状态计算出角色姿态。姿态就是美术K帧计算出角色最终的样子。

根据数据量和更新频率两方面来计算,这三部分中开销最大的是姿态部分。因为每个人骨骼都有朝向、旋转和位置等众多属性,并且它几乎是每帧都要变化。

目前,业界常见的优化方式也正是针对这一块进行的。其中最简单最直接了当的即LOD,它主要是减少了动画的数据量。例如对服务器判断受击没有任何作用的这些骨骼,我们把它去掉,一般可以减少20%-30%的开销。

除了减少数据量之外,我们还可以减少数据的更新频率。基于事件的姿态更新就是在减少姿态计算频率,例如《瓦罗兰特》就采用了这一技术。

简单来说,只有当角色被击中的瞬间,才会计算模型姿态,这种优化就极大的减少了姿态更新的频率。它可以把姿态更新的开销从84%直接降到9%。

做到这一步,你就可以在服务器上来跑动画了。可能会有一定的开销,但是不至于说完全跑不起来。这就是业界我们常见的一些优化方式

三、如何优化复杂的动画状态机

当姿态更新的开销降下来后,亟需解决的则是动画状态的更新。

如果一个角色有很多复杂的动画逻辑,那么导致状态机情况也会非常的复杂。这样的开销甚至会超过11%。接下来,我们将着重来讲述如何来优化复杂的状态机。

我们先看一下状态机是什么样子。以UE为例,首先它里面有走跑跳等一些状态。

例如从跳起来到落地的状态过程,它大概可分成三部分:第一部分Find_Transitions,角色从当前状态来找一个可能的跳转条件;第二部分,如果这个条件为真,有一个跳转可能发生,那么就执行这个跳转;第三部分,两种状态之间可能会有的一些过渡。

我们再看看如何优化。首先是状态过渡的优化。以上述Locomotion为例,至少有两种过渡模式。

一种是角色上一个状态权重逐渐降低,下一个状态权重逐渐升高,那这样权重会出现一个交叉。我们称之为Cross Fade。在此过渡过程中,它的两个状态权重都不为0,所以必须更新这两种状态。另外,如果两个状态中间又嵌套了别的状态机,也一定都要更新。

第二种方式,有的引擎称它为Immediate模式,或者叫做Inertialization模式。

简单解释下,假如角色还在空中跳,下一刻要落地,该怎么办。我把空中跳的这个状态拍个快照,直接不再更新它,接下来下一个状态的权重逐渐从0升到1。通过这种方式,我们只需要更新下一个状态。

基于这些优化之后,状态更新的开销大概能降低10%左右。那在寻找一个可能跳转的条件情况下,我们又该怎样优化?

还是以从空中到落地这段时间为例,如何决定接下来下一步需要着陆。这个条件有两种写法:

一种是直接接受了一个Bool值;第二种通过大于小于表达式判断角色速度是否发生变化,再来来决定是不是跳转。

根据UE官方的提示,这两种方法的效率大概会相差10倍。原因在于,前者直接使用Bool值来判断,会编译成本地代码。后者编译的是虚拟机代码,经过蓝图虚拟机来执行才能判断其中的结果。

需要提出的是,相比之下,Python、lua这样的非本地代码,性能本来就低。而过多地使用非本地化语言写条件,也会给状态机的更新造成很大的性能开销。当然,我们也可以通过人工的方式,将这部分判断转换成本地化代码,从而获得一定的性能提升

而在UE中,开发者可以使用Nativization等工具,自动化地将蓝图代码转换成本地化代码。对于动画状态机而言,这其实会带来10%左右的性能提升。

那还有没有更有效的优化方法呢?这就要在状态跳转环节下功夫了。

通过进一步分析,我们发现跳转条件其实也分为两类。一类是它依赖玩家的输入;另一种条件则是依赖动画播放进度。我们可以优化前者的更新频率,因为玩家输入频率不高,可以直接省掉更新。具体地,可以通过在UE蓝图中进行人工标注标明可优化的跳转信息。

在蓝图编译的时候,利用这些标注能够更容易把优化代码给插进去,这样最后生成的代码就是优化后的代码。经过测试,大概会有70%的优化。

四、服务器动画展望

总言之,虽然现在真正应用服务器动画的游戏并不多,但按照当前的发展趋势,这会是一个比较有前景的领域。它可以提供更公平的联机环境、实现更细致的交互。现在行业所提出的云游戏、元宇宙等,很难想象会没有动画。这也是服务器性能优化当下及未来的阵地。

本次的分享到此结束,谢谢大家。

2022N.GAME网易游戏开发者峰会4月18日-4月21日每天下午15点直播,戳链接可直达峰会官网:https://game.academy.163.com/event/nGame

关注微信