《天涯明月刀》服务器端3D引擎设计与开发


一、前言

随着用户对游戏品质需求的提升,MMORPG作为网络游戏中最重要的一种类型,也在潮流中不断的进化着。现在,网络游戏已经从之前的2D,2.5D时代,进化到了全3D时代。玩家可以在游戏中自由的轻功跳跃,飞行,上墙进屋,MMPRPG的玩法与体验得到了进一步的提升,更加接近于虚拟现实。

 

全3D时代的网游,对于网络游戏服务器提出了新的挑战。

 

面对新的需求与挑战,天涯明月刀项目在服务器端另辟蹊径,在提供不输于2D系统的性能的前提下,实现了一套全3D且逻辑安全的服务器端引擎。同时实现了性能,精度,安全三者的兼顾与平衡。

 

二、技术背景/业界现状

在客户端引擎方面,经过多年的发展,虚拟3D世界的构建已经有着较为成熟的技术。现在市场上有多款成熟的商业引擎诸如CryEngine,Havok,Gamebryo等可以借鉴与使用。

 

但是在服务器端,MMOPRG服务器无论是从用户模型,硬件现状,以及业务形态都与客户端有着巨大的差异:

 

a) 、硬件支持有限

服务器硬件少有像GPU类似的专用图形图像计算单元,只能依赖于CPU进行计算。CPU的浮点运算能力远远低于GPU

 

b) 、完全不同的业务模型

一般而言,MMORPG客户端需要同时精确计算的对象局限在较小的空间范围内(诸如九宫格视野范围内),数量较少,服务器必须接近实时地计算世界内的所有玩家所处的位置与状态

 

c) 、更多的计算需求

       在单个svr进程上需要支持3-4k玩家同时进行游戏

 

目前业界中,服务器端的3D解决方案有如下几种:

 

1) 、多边形网格(Polygon Mesh)

http://km.oa.com/files/post_photo/469/201469/3ecf16c248c3ee379404c536e31e3397.jpg

图1:基于Polygon Mesh的3D场景描述

 

将客户端的引擎移植到服务器端

优点

精度高:可以做到与客户端相同的精度表示

缺点

计算量大:碰撞计算非常消耗CPU

资源量大:需要在服务器端导入大量的美术资源

开发难度大:要讲通常跑在客户端的引擎代码移植到服务器端,迁移成本较高

 

结论:

l        性能上无法满足MMORPG大世界的承载需求

l        可以通过增加机器的方式对性能需求做出一定的缓解,但是会拉高整体的运营成本

l        通过降低精度可以缓解计算压力,但是精度不再的话就丧失了唯一的优势

 

 

2) 多层网格(Layer Gird)

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image003.jpg

图2:基于 Grid Layer的3D场景描述

 

通过添加多层扩展传统的服务器2D网格场景描述

 优点

计算量小无需计算复杂的碰撞,只需检查网格碰撞即可

开发难度小大部分的2D行走逻辑可以继承,只需要在切层时做额外处理

 缺点

资源生成困难,描述能力不足:

复杂建筑用层来分割非常困难,数据生成难度很大。

 

如下图:

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image004.jpg

 

上图中循环上升的回廊,回廊在不同高度上究竟属于那一层?

 

重叠的部分理论上应当属于不同的层,但是按照多层网格数据格式的定义,又应该属于同层。在描述上会出现矛盾的尴尬境地。

上面的例子很实际的说明了多层网格的描述能力是有限的,而且遇到复杂建筑的时候,如何正确的描述建筑规格,会遇到很大的困难。Case by Case的解决也无法完全消除问题,而且效率很低。

 

精度较低:由于采用固定规格的格子大小,在描述一些形状较小的复杂物件的时候会存在精度问题。

        

结论:

l        性能上可以满足承载需求

l        数据制作难度过大,且存在描述能力不足的问题,无法满足内容制作需求

 

三、设计方案

3.1、Voxel概念的引入

Voxel即体素(Volum Pixel)的简称。Voxel体素的概念是从二维空间的最小单位像素衍伸而来。像素用于描述二维的影像,是二维空间上的最小单位。而体素则可以用于描述三维空间上的立体的对象,是三维空间分割上的最小单位。

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image005.png

图3: Pixel 描述的二维图像

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image006.jpg

图:4:Polygon Mesh描述的三维对象

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image007.png

图5: Voxel描述的三维对象

 

Voxel描述相较于Polygon Mesh的优势在于:

 

性能

Voxel本身是规格化的,无需使用浮点数运算,避免了服务器CPU浮点运算能力的瓶颈。

通过合理设置精度范围(Voxel的大小),可以大大降低服务器的计算频率,减小计算压力,同时保持比较好的C/S同步。

 

资源

相比Polygon Mesh,资源描述简单,服务器使用规格化的数组即可描述,加载,解析,存储都很方便。

 

开发难度

Voxel本身就是在2D基础上的概念扩展,从2D系统迁移到Voxel系统的过程中,迁移成本较低。且其中原有3D系统阻挡Mask的概念还可继续沿用。

 

如何用Voxel来描述复杂场景,让我们在下一节详细展开。

 

3.2、 基于Voxel的3D场景描述

使用Voxel对场景进程3D描述的具体方案如下:

l          将3D场景以Voxel为单位进行离散化描述

l           (x, y, layer) 三元组对场景描述voxel的位置进行定位

l           (x, y, z, layer) 四元组描述移动对象在空间的位置

l        (layer为冗余量,用于快速定位同一垂直投影上的Voxel对象)

l          layer = 0 的voxel为地表层,voxel记录上沿(upward)高度

l          layer > 0 的voxel为建筑层,voxel记录上沿(upward)与下沿(downward)高度

 

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image008.jpg

图6:室内室外场景Voxel描述剖面图

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image009.jpg

图7:拱桥Voxel描述侧视图

 

上面的Voxel示意图中,绿色的Voxel为0层,黄色的为1层,蓝色的为2层。

 

可以看到,视觉上处于同层的蓝色与黄色,实际上是处于不同的逻辑层上的。也就是说,在Voxel方案下,层的概念只在水平grid的垂直投影面上才有意义。相邻的Voxel之间,层id相同,并不表示联通/或相接,因此,原有的行走检测算法已经不再适用,需要引入基于Voxel的碰撞检测算法

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image010.jpg

图8:天涯明月刀场景常规视图

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image011.jpg

图9:天涯明月刀刀场景Voxel视图

 

3.3、基于Voxel的碰撞校验算法

通过对玩家在游戏世界中的移动行为进行研究,我们可以划分并得到两种行为模式:行走与轻功。

 

3.3.1、行走

玩家在行走时,脚步不会离开地面。用voxel的视图来表述的话,玩家的移动就是在不同voxel之间切换,且玩家的高度(z轴坐标)始终是当前所在Voxel的上沿的位置。

既然玩家的高度是由所在Voxel确定的,那玩家在行走的过程中,其实是不用处理高度信息的,以(x, y, layer)三元组即可描述/处理玩家的移动路径。

 

3.3.2、轻功

轻功过程中,玩家的脚步脱离了地面,即玩家的移动不再限制于Voxel的上沿表面。此时,需要(x, y, z, layer)四元组来描述/处理玩家的移动路径。

 

通过对于移动模式的分解,我们可以进一步细分碰撞模型:

3.3.3、行走 

行走的碰撞校验步骤为: 

1、  Grid Mask判定

Ø  检测Voxel对应的Grid Mask

Ø  静态阻挡、动态阻挡、水面阻挡等

 

2、  高差判定

Ø  相邻Voxel的高度差是否在合理范围内

Ø  高差判定的方向性:

Ø  从高处可以往地处走无需考虑高差

Ø  低处往高处走,需要考虑高差,太高走不上去

 

3、  碰撞判定:

目标Voxel之上的高度空间是否能容纳移动对象

 

经过这三步,就可以完整的判定移动是否合法。下面是流程示意图:

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image012.jpg

图10:行走逻辑碰撞判定规则

 

3.3.5、轻功

轻功的碰撞检测步骤为:

1、  Grid Mask判定

Ø  检测Voxel对应的Grid Mask

Ø  空气墙,动态阻挡

 

2、  碰撞判定

Ø  目标Voxel上的空间是否可以容纳移动对象

Ø  移动路径前方是否有Voxel阻挡

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image013.jpg

图11:飞行逻辑碰撞判定规则

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image014.jpg

图12:碰撞判定示意图

 

 

3.4、服务器端3D引擎构架

通过封装3D场景管理数据读取以及碰撞算法,天涯明月刀server形成了一套完整的以voxel描述数据为核心的服务器端3D引擎构架。

 

示意图如下:

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image015.jpg

图13:服务器端3D引擎构架示意图

 

3D场景引擎:

Ø  数据管理模块位于底层,提供了基础的地形碰撞信息读取接口。

Ø  在底层Voxel数据管理的基础上,实现了多层Layer的管理。

Ø  Voxel数据与Layer Patch(详见下文)数据统一放置到场景数据管理器之下。

Ø  碰撞校验API以及场景数据API访问场景数据,并向上层提供服务。

Ø  同时提供一套运动计算引擎,封装了常规的运动计算公式的实现,可以供上层移动逻辑调用。

 

移动控制模块: 

Ø  提供行走,轻功,拖拽等多种方式的移动模型,可以充分满足业务逻辑的开发。

 

业务逻辑: 

Ø  AI,玩家移动以及技能位移作为最上层的逻辑,直接调用移动控制模块提供的接口即可,无需关心底层实现,简化了一线业务开发的工作量。

 

四、性能优化

在之前的技术方案评估中,已经论证了Voxel方案比起Polygon Mesh方案有着较大的性能优势,但是相较于之前的2D移动方案,性能还是有大幅度的下降。

 

为了实现单进程3-4k玩家数的承载的目标,从用户模型与数据特性出发,我们对Voxel方案进行了细致的优化,最终实现了和2D移动方案下相近的性能。

 

4.1、时间优化

4.1.1、多层索引Cache(Layer Mask Cache)

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image016.jpg

图14:多层索引示意图

 

要点

碰撞检测算法中存在大量指定(x,y),获取此grid垂直投影面上所有层信息的操作。经过统计此处操作函数为热点函数,且占用了大量的cpu时间。

 

针对这个问题,可以通过预先生成并缓存layer mask,在运行时访问layer mask缓存,即可快速定位并获取grid垂直投影面上层的信息

 

副作用:

内存消耗增加,地图大小为4km x 4km,且每个voxel平面大小为50*50的情况下,需要额外占用64M的内存

4*1024*100 / 50  = 8192;  // 地图以voxel为单位的边长

8192 * 8192 = 64M        // 整体内存占用

 

与不优化的情况相比,单张地图资源内存使用增长了15%左右,从整体性能trade off来看,还是划算的。

 

4.1.2、连通性Cache(Connectivity Cache)

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image017.jpg

图15:连通性cache示意图

 

要点

为了美术数据制作限制,导致出现穿墙等行为,在3D引擎内部,移动对象的连通性判断限制为在相邻grid之间的四方向之间进行。外部传入的路径会经过转换生成一条在四方向之间进行移动的路径。

 

通过仔细分析我们可以发现,相邻grid之间,同一个方向上,从src层只能切换到一个唯一的dst层,即相邻grid之间的连通性是唯一的。

 

由上面的分析可以看到,决定连通性条件都是静态的,在运行时进行碰撞检测来计算连通性,实际上存在大量的重复计算。

 

同时,经过统计,90%以上的地表只有0,1层,我们只需要对这两层的碰撞判断做针对性的优化,即可覆盖绝大部分的计算。

 

综上,我们提出了一套基于联通性Cache的优化方案:通过预先计算碰撞信息,生成全图的连通性Cache,在运行时直接访问Cache获取连通性信息,避免重复计算,可以极大提升运行性能

 

优化效果

 Ø  相邻Grid连通性碰撞检测平均时间消耗从2000 CPU周期降低至200

Ø  场景服务器整体CPU消耗降低30% (单进程3000人在线战斗 + 移动)

 

副作用

内存消耗增加,4km x 4km 的地图需要额外占用128M的内存。

与不优化的情况相比,内存使用增长了30%左右,虽然在时间优化方面有了非常大的提升,但是将压力转移到了内存空间使用上,下面我们会针对这种情况做进一步的空间优化。

 

4.2、空间优化

 

天涯明月刀刀的地图大小是同类游戏中最大的(4km x 4km),按照之前定义的Voxel模型,我们可以推算一下,每张大地图的内存使用量:

Ø  大地图面积:8192 * 8192(4 km)

Ø  Mask+上下沿高度: 5 byte

Ø  全图最高建筑层数:8

Ø  场景地图数:8

Ø  单张地图所需内存:2.5G

所有地图需要内存:2.5 * 8 = 20 G !

 

除此之外,还需要加入之前因为时间优化需求而加入的各种cache数据,对内存的需求会进一步膨胀,这显然是无法接受的。为此我们必须要展开内存使用优化。

 

4.2.1、多层补丁(layer patch)

首先我们观察到,地图大部分都是野外(0层, 90%),只有城市中才存在多层建筑。这种情况下,0层以上大部分的数据都是空的,没有这部分内存被完全浪费掉了。

 

基于这样的数据特性,我们提出了多层补丁(Layer patch)的数据组织方案,来实内存优化。

 

多层补丁,即为0层以上的地形,不已全图的形式展开,而是以补丁的形式存在,有数据的地方才有补丁。诸如下图:

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image018.png

图16: Layer Patch示意图

 

layer 1可以简化为两个patch,layer 2可以简化为1个patch,数据量大大降低,从而节省了更多的内存。

 

优化后:

 

Ø  0 层: 8192 * 8192 * 3 = 192M

Ø  2 patch层,每层有100个patch,每个patch大小是 50m * 50m

Ø  100*100*5*100*2  = 10M

Ø   

单张地图所需内存  200+M

 

通过layer patch的优化,显著降低了内存使用量,使得方案达到了可用的程度。

 

4.2.2、多进程地图资源共享

在天涯明月刀超大地图的设定下,经过patch优化后,单场景服务器内存使用依然达到16G(其中地图6G)。如此一来,B6机器只能支持部署3个场景服务器(scened),而B6机器上有12个计算核心,多核计算资源被大量浪费。

 

然而仔细分析,我们的多个scened进程其实都是同构的,其中的地图数据也是相同的,且这部分数据都是只读的资源数据。在同一台物理机上的多个scened各自都加载了一份庞大的地图数据,如果能把这部分数据做成共享的,那我们将节省出大量的内存资源。

 

在这样的情况下,我们提出了一套多进程资源数据共享的方案:

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image019.jpg

图17:多进程内存共享示意图

 

优化方案:

Ø  将资源数据从单个场景各自加载改为统一加载至共享内存中

Ø  各个场景服务器attach至共享内存,使用同一份数据

 

优化后:

Ø  单机整体内存占用下降30%(3 scene 部署情况下)

Ø  5 scened机部署可以改为单机部署

 

4.2.3、开发模式地图资源按需加载

天涯明月刀团队有着140人的规模,针对大项目组多人协作的情况,我们设计了一套完整的服务器私服部署/版本分发体系,力求项目组中的每个人都可以拥有一个自己可以控制,调配的私服,实现无干扰的高效开发。

 

但是我们的场景进程在dev模式下内存需求达到14G,导致B6机器只能支持部署3组私服,140人规模团队,私服需求巨大。目前我们有12 台vb4,6 台A5,2 台B6作为私服,但是还是无法有效满足团队需求,内存紧张一直困扰着我们。

 

针对这种情况,我们针对dev模式设计了一套地图数据按需加载模式。

地图数据按需加载,dev模式启动时只加载一张默认地图。采用先全量加载,再释放冗余地图的策略,保证资源校验的完整性。然后在发生传送/进入新地图事件时,按需动态加载地图数据。

 

优化结果:

Ø  单scene内存需求下降至8G

Ø  以20台左右服务器支撑起100余套私服

Ø  有效支持全项目组的日常开发工作

Ø  解决以后新增地图情况下私服内存紧张问题

Ø  为公司节省大量dev-net机器资源

 

4.2.3、性能基线

最终的优化完成之后,我们实现了单个进程支撑3000人同时移动下场景服务器压力维持在20%以下的目标。

 

http://avocado.oa.com/fconv/files/201405/958e75f09f7ea5617c5e127837fa7d07.files/image020.jpg

图18:服务器移动性能基线表

 

五、结语&挑战

通过这套服务器端3D引擎,有效支持了天涯明月刀的玩法与功能特性开发。且为工作室日后的新项目提供了开发与借鉴的基础。

 

但是不足之处还有很多,在未来的日子里,sever组会进一步强化引擎的功能,以在玩法开发方面提供更多的支持。

 

未来可能会考虑引入可变精度的Voxel来实现更高精度的场景需求;同时也会实现动态建筑建造与升级等功能,还请大家拭目以待!

猜你喜欢

转载自blog.csdn.net/icebergliu1234/article/details/80441335