带宽限制下的视觉实体属性传播

  The Sage of Ryzom是一款在2004年9月发布的MMOPRG,最开始发布在欧洲和北美,目前已经被本地化为3种不同的语言。它由Nevrax在2000年开发,在2006年末时由Gameforge接管。

  Ryzom由Nevrax团队独立开发,包括了Nevrax Library (NeL),该库基于GPL协议发布。在NeL之上,开发出了一项服务技术用来处理高度仿真的虚拟世界。这篇文章关注于所开发出的如何平滑地移动实体和将动态属性传播给所有相同区域内其他客户端的网络技术。

  Daniel Miller,Nevrax和Gameforge France的CTO,感谢他所做出的巨大贡献和对这篇文章的最终审查。

  工作目标

  我们的客户端软件需要显示一个包含移动实体及其动画的3D动画场景。玩家会通过类似鼠标点击等行为来直接控制自己的角色。与需要对移动完全进行同步和平滑显示的第一人称射击游戏和体育游戏不大一样,角色扮演类游戏通常并不需要非常快速的输入处理。

  在Ryzom 项目启动的时候,其他游戏中的实体经常会出现一些位置回退和跳跃前进的情况,我们希望在我们的游戏中,这类可以看见的位置跳转或者位置颠簸情况要比其他MMOG少。另一个重要特性是允许玩家在一个无缝的环境中移动,不会出现一些预先定义的小地理区域的加载过程,因为在这些加载时间里,玩家只能停下来等待。

  在这篇文章中,将会解释一些我们曾经尝试过和最终我们所选择的技术。视觉属性是指客户端软件在渲染玩家或NPC实体时所需要的任意的动态状态或者描述属性, 比如位置和头顶文字,3D模型ID,服装,当前播放的动画,等等。我们假设客户端软件能够在本地获取到类似于3D模型,贴图和动画这些静态数据。这篇文章的大部分内容将重点关注于位置的广播,因为当平滑自然的位置路径广播未实现时,客户端将非常容易地看出来。

  挑战

  对一个在线游戏,特别是一个大量玩家同时在线的游戏来说,带宽的限制将使得我们不能直接将所有的位置改变消息发送给所有的玩家。在Ryzom项目立项的时候,56K调制解调器在玩家中仍然被广泛使用,ADSL仍处于起步阶段。我们的MMORPG必须在56K甚至14K的连接下工作。

  此外,对一款在线游戏来说,作弊行为完全地毁掉了受害者的游戏体验,所以必须有技术手段来防止这类行为的出现。“不要相信客户端”已经成为一句越来越流行的格言了,而最近Nevrax创始人的计划是要将Ryzom客户端在自由软件协议之下发布,这更加迅速地显示出客户端只是一个显示和输入设备,所有的游戏逻辑都必须在服务器端完成。

  在服务器端处理动态的游戏信息将有效地防止作弊行为的出现,比如一些玩家制作的用于偷看其他玩家数据的雷达。然后,避免使用点对点的网络方案,这样也导致带宽的限制不仅仅只出现在使用低带宽的玩家电脑上,在租用带宽的服务器机房也会出现。

  MMORPG的游戏体验与延迟是联系在一起的,经常在发出的动作指令与其显示效果之间出现一段可以看见的而且令人非常不愉快的停顿,有时是动画停止,有时是动画颠簸。这种延迟现象必须通过设计来使其最小化。它通常是由于在严格的带宽限制和高延迟的网络环境下使用了不合适的信息广播系统而导致,也有可能是由于服务器的CPU处理导致了延迟。

  我们的工作就必须关注于学习当前已知的视觉属性传播技术,并且尽可能地创建一个更好的技术。为了避免高的时间开销,软件的CPU性能也是很重要的。

  导航预测算法 -- 推理

  早期的分布式仿真项目,比如DIS,只处理具有高度惯性行为的移动对象。在这项飞机飞行模拟实验中,只是标准的匀速直线运动,然后做一些渐进的变化。这样它才能通过模拟一个实际实体的复制品来复制其移动,并且应用一些行为改变事件。

  比如,如果实际的实体降低速度s,一个更新数据包将会发送给它的复制体以使其也降低速度s。当然,这个更新包将会有一段无法避免的传输时间。在更新包到达之前,该复制体对象的位置还在继续按原来的速度改变,这样就必然导致了需要有一个位置修正,也使得在复制体上观察到的对象移动路径与其实际路径会有一些轻微的偏差。

  这种位置上的偏差可以通过在更新包中引入额外信息来降低:比如一个表示速度从什么时候开始改变的时间戳可以使得复制体的移动路径最终与实际路径合并上,虽然在更新包传输的过程中,复制体还是会产生一个不大一样的路径。

  这样,我们需要实现一种算法来将临时的不准确路径与其正确的路径进行混合,最终的结果依赖于混合的深度(比如可以使用之前收到的位置历史数据)。

  为了降低更新的频率,我们可以只在原始路径和复制体的移动路径发生指定数量的偏离时才发送更新包(这可能需要在主处理器上同时运行主实体和其复制体的运动模拟,以用来对这两条路径进行比较)。现在我们就有了一个导航预测系统,该系统以古代航海学上在星星不可见时用来导航的技术而命名。

  但是,在大多数MMORPG中,主要被控制的对象都是人类角色,他们的运动都是高度随机的。使用导航预测算法可能会导致非常频繁地发送状态校正更新包,网络流量和视觉上的震动都会大大增加(因为当混合后的复制体路径与原始路径相差较大时,有时候不得不做一些迅速的位置跳转)。

  导航预测算法仍然可被用于交通工具,AI控制对象这类具有大体上固定移动规律的实体。为了能够处理大量玩家控制的实体,我们转向了另外一种不同的策略:传输"good-old"位置更新包,同时使用插值的方法模拟一段时间内的自动移动,并且保持对更新频率完全控制。

  虚拟的时间空间 - 插值

  Vavle的Yahn W. Bernier在2000年的游戏开发者大会上做过一段关于Half-Life & Team Fortree Networking的发言。其中有一个想法是,除了试图预测未来之外(也就是导航预测算法的原理),为什么不可以将过去的情况作为现在来处理呢?当一个客户端接收到场景内所有其他实体的所有更新包后,他们就可以非常平滑地显示出场景的实际动画。

  这也是为什么会有一个延迟,也被称为滞后补偿时间(LCT),被引入到实际动作和在网络传输管道的另一端显示之间。LCT越高,所接收到的更新包也越多,移动也就会越平滑:如果一个角色到达了一个位置,但这时从服务器发来的另一个位置更新包又还没到,那么它将不得不停下来等待。

  这将导致停停走走式的移动,特别是当位置更新包发送频率不规则时,这也是在通过internet发送数据时无法避免的问题。LCT的目标就是确保这样的情况不会发生,但是LCT又需要尽可能的小,因为它代表着滞后或时间不一致的情况。

  如果两个玩家的屏幕挨在一起,他们显然会发现,他们的显示位置一直在交换。在另外一些情况下还可能会注意到:比如,当两个角色试图同时移动时,他们会感觉到另一个玩家一直在自己后面,因为他们控制的角色一直都没有发生位置交换(那将会是一个非常严重的操作体验)。

  这也产生了一个显示在当前时间空间的对象(玩家自己控制的角色)与显示在过去时间空间的对象(远程角色对象,AI控制实体)之间的交互问题。一种解决方案是让LCT随着与角色对象的距离而变化。这一方案被称作暂时感知,或呈现时间,或者有时候叫做本地感知过滤,这是来自于对天空中星星出现情况的推理:距离越远,光线到达我们这里需要的时间就越长。

  无论如何,如果我们希望使玩家附近的实体比远处的实体更准确地移动,那我们就应该将观察者附近对象的更新包的优先级设置高一些。目的是为了最大限度地减少误差幅度:跟前的一个实体(如正在战斗中的对手)最多只能出现1米以内的位置误差,而远处的一个实体尽管位置偏离了好几米,但显示出来的结果可能只有几个象素距离的误差,甚至还会更少。远处的实体应用低频率的位置更新,这样需要的LCT就可以比较大。灵活的LCT可以尽可能地减少近处玩家的延迟,同时也能够避免远处玩家实体的位置颠簸。

  这也意味着我们需要一种方法来控制被观察对象的更新包的发送频率。

  时间同步

  在多台机器之间维护时间需要一种同步机制。通常的同步方案是,在本地机器时间与服务器时间之间计算一个差值和参考时间,然后传输与参考时间之间的时间戳。但是,我们注意到一些机器的内部时钟速度不一样,这样会导致错误的同步。

  此外,我们的服务器应用程序采用了一种灵活的时间系统,该系统基于一个可发送”ticks”的指挥服务,这样可以以所有的服务器应用程序都接受的比率增加当前游戏循环的速度:如果一个服务突然增加了工作负载,所有的服务都可以停下来等它,以避免恶意循环的阻塞。客户端时间同步因此基于收到的两个消息包的平均时间,假设服务器定期地发送数据包到所有的客户端。

  更新频率控制 - 优先级策略

  虽然大家都认为消费者的调制解调器和带宽在未来几年内会增加很多,但是保持一个低的传输率仍然具有降低带宽使用费用的优势。毕竟,运行一台支持数以千计的玩家同时游戏的服务器(这也是MMOG的本质)仍然需要很大的网络连接,而这也不可避免地是相当的昂贵。

  在一个比较热闹的3D场景中以13kb/s的速度(一般设置的瓶颈值)传输位置更新包也给我们带来了以下问题:

  * 哪些更新包具有高的优先级

  * 需要的CPU效率是多少

  我们也尝试了多种算法,他们的基本原理也都是相同的。对一个特定的观察者来说,在一个给定的时间:

  1. 在无缝的世界环境中决定哪些实体在观察者的周围,并且发送这个列表的更新到客户端。

  2. 根据距离来为列表中的这些实体指定优先级。

  3. 根据优先级高低顺序遍历这些实体, 比较他们当前的服务器端状态与客户端保留状态的差别,添加更新数据包到缓冲区,到缓冲区大小达到带宽瓶颈时停止。

  计算可见实体

  Ryzom中使用了两种主要的算法。

  第一种被用于Ryzom的大陆。空间被一个网格所划分,首先获取与要计算的实体在同一网格的实体,然后以螺旋状的方式遍历周围的网格,并添加其中的实体,直接找到足够数量的实体。

  对于Ryzom Ring副本来说,区域一般都比较小,所以采用了一种新的算法:根据实体当前位置与组重心的距离来创建动态组。如果实体移动到离组很远的地方,就将组一分为二:创建一个新组出来。

  这两种情况的结果都是相同的:每个客户端游戏实体都与一个其可见的实体集合相关联。解释这些算法的细节已经超出了本篇文章的范畴,但接下来我们将看到这些关联和他们的属性是如何以及何时发送给客户端的。

  优先级:信息的相关性

  我们曾经尝试过的一种算法是通过以下方法构造的:试图将一个属性的更新频率与该属性更新所引起的观察者屏幕上受影响的象素数量相关联。实际上,第一个基于预算的算法被开发用来处理每个属性:每个元组(观察者客户端,观察的实体,属性)被赋予一个优先级,还有一个数据结构用于表示"架子和桶",这些在发送更新包到客户端时会被浏览到。优先级帮助将一个属性的改变事件映射到一个正确的桶上。它是通过一些标准计算出来的:

  从观察者到实体的距离

  客户端所知道的状态拷贝与其实际数据之间的差值

  对于位置来说,我们解决了当实体移动了一段很长的距离而实际差值却非常小时可能会引起的问题:比如,当在墙上行走时,开始点和结束点可能会非常接近,而在这种情况下的改变又是很重要的。我们通过比较”移动的数量”来代替移动的位置,这样我们也就不得不在每次检测到实体移动之后都进行累积。

  其他属性也同样被列入考虑。比如,如果一个被观察实体突然戴上了他的帽子,而并没有移动,这个实体也同样会获得一个比较高的更新优先级。

  这个算法在我们25*250*1000个元组(每个前端服务有1000个客户端,每个客户端显示250个实体,每个实体有25个动态属性)的目标上被证明是太过于耗费CPU资源的。因此,另外一个不同的算法又被开发出来,通过下面的标准为每个元组(观察者,被观察的实体)计算分值:

  观察者到实体的距离

  实体分值的增长与它到观察者的距离成反比,当状态更新包被发送到观察者的客户端后,分值被重置。另外一个步骤用来对特定的被观察的实体进行处理,以决定哪些属性需要发送给观察者。

  决定哪些属性需要被发送

  当根据优先级遍历被观察的实体时,我们会将实体当前的属性与保存下来的上一次发送更新时的属性值进行比较,发送改变过的部分。如果他们不匹配,我们会在有足够的资源时将他们包含进更新数据包中并且发送。上一节中描述过的路程跟踪被保留用来做位置仲裁。这个比较也是一个非常关键的部分,因为其被调用的次数非常多,我们也使用了相当多的手段来对其进行优化。我们随后通过仅比较那些与实体类型有关的属性来对整个步骤进行了优化。比如,我们提前已经知道在Ryzom中智能植物是一种位置固定的实体,这样我们就不需要比较它的位置属性是否已改变了。

  优化

  为了减少一个游戏循环中所做的操作的次数,我们添加了一个用来将游戏循环分隔为多个游戏循环的系统。这样,一次只会有一部分的可见实体做优先级计算。

  另一项优化策略在于使用多处理器的优势:计算部分和发送部分被安排在不同的线程中。

  低级的优化处理也做过:通过显示c++编译器生成的汇编代码,我们重新设计了数据结构,以使得处理器在获取这些数据的时候尽可能地减少缓存未命中的情况。

  很抱歉,因为您在网易相册发布了违规信息,账号被屏蔽。被屏蔽期间他人无法访问您的相册。

  去帮助中心,了解如何重新恢复服务。

你可能感兴趣的:(数据结构,游戏,算法,优化,网络,服务器)