一、前言
随着用户对游戏品质需求的提升,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)
图1:基于Polygon Mesh的3D场景描述
将客户端的引擎移植到服务器端
优点:
精度高:可以做到与客户端相同的精度表示
缺点:
计算量大:碰撞计算非常消耗CPU
资源量大:需要在服务器端导入大量的美术资源
开发难度大:要讲通常跑在客户端的引擎代码移植到服务器端,迁移成本较高
结论:
l 性能上无法满足MMORPG大世界的承载需求
l 可以通过增加机器的方式对性能需求做出一定的缓解,但是会拉高整体的运营成本
l 通过降低精度可以缓解计算压力,但是精度不再的话就丧失了唯一的优势
2) 、多层网格(Layer Gird)
图2:基于 Grid Layer的3D场景描述
通过添加多层扩展传统的服务器2D网格场景描述
优点:
计算量小:无需计算复杂的碰撞,只需检查网格碰撞即可
开发难度小:大部分的2D行走逻辑可以继承,只需要在切层时做额外处理
缺点:
资源生成困难,描述能力不足:
复杂建筑用层来分割非常困难,数据生成难度很大。
如下图:
上图中循环上升的回廊,回廊在不同高度上究竟属于那一层?
重叠的部分理论上应当属于不同的层,但是按照多层网格数据格式的定义,又应该属于同层。在描述上会出现矛盾的尴尬境地。
上面的例子很实际的说明了多层网格的描述能力是有限的,而且遇到复杂建筑的时候,如何正确的描述建筑规格,会遇到很大的困难。Case by Case的解决也无法完全消除问题,而且效率很低。
精度较低:由于采用固定规格的格子大小,在描述一些形状较小的复杂物件的时候会存在精度问题。
结论:
l 性能上可以满足承载需求
l 数据制作难度过大,且存在描述能力不足的问题,无法满足内容制作需求
三、设计方案
3.1、Voxel概念的引入
Voxel即体素(Volum Pixel)的简称。Voxel体素的概念是从二维空间的最小单位像素衍伸而来。像素用于描述二维的影像,是二维空间上的最小单位。而体素则可以用于描述三维空间上的立体的对象,是三维空间分割上的最小单位。
图3: Pixel 描述的二维图像
图:4:Polygon Mesh描述的三维对象
图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)高度
图6:室内室外场景Voxel描述剖面图
图7:拱桥Voxel描述侧视图
上面的Voxel示意图中,绿色的Voxel为0层,黄色的为1层,蓝色的为2层。
可以看到,视觉上处于同层的蓝色与黄色,实际上是处于不同的逻辑层上的。也就是说,在Voxel方案下,层的概念只在水平grid的垂直投影面上才有意义。相邻的Voxel之间,层id相同,并不表示联通/或相接,因此,原有的行走检测算法已经不再适用,需要引入基于Voxel的碰撞检测算法
图8:天涯明月刀场景常规视图
图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之上的高度空间是否能容纳移动对象
经过这三步,就可以完整的判定移动是否合法。下面是流程示意图:
图10:行走逻辑碰撞判定规则
3.3.5、轻功
轻功的碰撞检测步骤为:
1、 Grid Mask判定
Ø 检测Voxel对应的Grid Mask
Ø 空气墙,动态阻挡
2、 碰撞判定
Ø 目标Voxel上的空间是否可以容纳移动对象
Ø 移动路径前方是否有Voxel阻挡
图11:飞行逻辑碰撞判定规则
图12:碰撞判定示意图
3.4、服务器端3D引擎构架
通过封装3D场景管理数据读取以及碰撞算法,天涯明月刀server形成了一套完整的以voxel描述数据为核心的服务器端3D引擎构架。
示意图如下:
图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)
图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)
图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层以上的地形,不已全图的形式展开,而是以补丁的形式存在,有数据的地方才有补丁。诸如下图:
图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各自都加载了一份庞大的地图数据,如果能把这部分数据做成共享的,那我们将节省出大量的内存资源。
在这样的情况下,我们提出了一套多进程资源数据共享的方案:
图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%以下的目标。
图18:服务器移动性能基线表
五、结语&挑战
通过这套服务器端3D引擎,有效支持了天涯明月刀的玩法与功能特性开发。且为工作室日后的新项目提供了开发与借鉴的基础。
但是不足之处还有很多,在未来的日子里,sever组会进一步强化引擎的功能,以在玩法开发方面提供更多的支持。
未来可能会考虑引入可变精度的Voxel来实现更高精度的场景需求;同时也会实现动态建筑建造与升级等功能,还请大家拭目以待!