关于联网游戏的聊天功能,想必玩游戏的小伙伴们都在里面喊过话吧,下面我们来聊一聊网络游戏架构的那些事儿。

一、世界喊话

    首先我们知道一般简单一点的聊天室的实现方式是你发一条消息广播给所有人,这样大家就好像都在一个屋子里互相都能看到对方的发言。很多大学、专科的学生都实践过这类功能。

    这种聊天室的工作模式可以用下面这张图来表示,一般我们实现这类功能只要服务器收到消息之后把消息分发到所有客户端上就可以了。服务器上只需要维护一张全局用户表就可以。

泰课在线

    有了聊天功能,现在游戏中的玩家终于可以开口说话了,只不过这个世界比较赤裸裸没有什么隐私可言而已。

二、密聊

    世界如果总是那么赤裸裸的,那要让游戏里的小情侣们怎么过日子呀。小情侣们之间羞羞的话题怎么好让所有人都看到呢。于是除了大家在一起互相聊天之外,还要有密聊的功能。

    密聊这个功能本身的特性就是聊天对象有着非常明确的目标,就是 A 到 B 两个玩家之间单向的消息传递。服务器在转发这类消息的时候就可以不用去循环便利所有玩家,只需要找到特定的玩家把消息丢过去就可以了。实现起来也不难。

三、小队频道

    ok 这个系统目前可以让我们的游戏玩家可以互相自由的聊天了。但是我们知道玩家一旦多了,就会发生大家的聊天变成了刷屏。于是我们开始为不同目的的玩家划分频道,比方说玩家A 聚集了 5个小伙伴组成了一个小队一起去打 boss。于是聊天室里就多了一个概念叫做频道。大家聊天可以根据不同频道进行聊天。

    而频道于全局聊天的用户列表不同的是,在频道中只有有限的几个玩家。so,频道也是就是几个玩家的列表而已。给频道内的玩家发消息就变成了循环频道列表发消息,实现起来也不难。

 

    频道的出现可以非常方便的解决一小部分人的组团聊天需求,要想实现这类功能首先我们要在服务器上先要能动态的创建出消息容器,当两个人以上完成组队的时候,我们就可以用队伍的 ID 来充当 队伍频道的标示符。不同于广播的是,我们在整个游戏聊天服务器中相当于创建了多个小聊天室。剩下的就是只要队伍中的人在发队伍消息的时候,只需要带上频道 ID 为队伍 ID 就可以了。不同的队伍拥有不同的小聊天室,这样就可以完美解决频道聊天的问题:

泰课在线

四、InBox

    这个时候游戏的聊天系统稍微复杂了一点,已经有全局喊话、密聊、频道聊天。这么多种的聊天消息的来源都要接入,不同的频道还可能存在加入和退出的情况。这个时候我们需要为消息的传递设计一种工作方式,这样传递消息才能尽然有序。

泰课在线

    喊话的玩家把喊话的消息发送到 公共聊天 InBox,公共聊天 Inbox 接到消息之后再把这个消息转发所有玩家。队伍聊天时把消息发送到自己的队伍 InBox 里,然后队伍的 InBox 在把消息转发到队伍里的玩家。

五、本地聊天

    ok,到了这里似乎整个世界都可以正常流转了,大家也不用满世界的刷屏喊话。可是玩过游戏的玩家都知道,一般我们在城里的时候通常可以看到很多玩家在相互聊天,一旦是当我们远离城市去做任务的时候就收不到那么多聊天信息了。

    如果周围只有你自己一个人的时候,通常聊天框里就只有战斗信息。但是我们自己心里都知道这并不代表别人在城里没有说话,而是已经收不到他们的聊天信息而已,这就是本地聊天的作用。

    还有一种本地聊天的 case, 离你附近不远的人说话你可以看到,但是他离你足够远的时候就收不到聊天消息了。本地聊天更像是人在现实环境中所能听到范围,离你太远的人说话是听不到的。

泰课在线 泰斗社区

    让我们想一想,这种情况如果每个人都是如此会是怎样的一种场景(下面列出两个人的,如果更多呢?)

泰课在线

 

    在图中只要发送消息的人,每次在发送聊天消息的时候确定一个范围,并且把消息发送给这个范围内的其他玩家,就可以实现本地聊天

    要实现本地聊天,其实就是筛选距离符合条件的,那么我只需要计算两个聊天人之间的距离就可以了。初中的知识,直角三角形计算公式这回终于排上用场了。

泰课在线

    假定我们接收消息的半径是 R,那么位于蓝点位置的玩家能否接收到红点位置玩家发出的消息就变成了。计算 红蓝之间距离是否大于 R 的算数问题。

六、3D下任意两个点之间的距离

    二维情况下我们可以画圆圈,3D情况下就要画球。下面我们开始画球,一个玩家一个球,两个玩家两个球,一堆玩家就是一堆球。画球是因为我们要考虑虽然两个玩家都站在一个平面坐标上,但是由于高度不同,彼此太远也会收不到对方的消息这种情况。 否则 3D 模式下画的就是圆柱了。

    在 3D 情况下计算任意两个点之间的距离计算会比较复杂,首先任意两个点之间可以看成盒子的两个对角线。求两点之间的距离就是求两个点所处平面的直线距离。为了得到这个平面我们需要先把这个盒子用刀切成一个三角形的奶酪。有了这个奶酪就可以计算红蓝的距离了。所以整个计算会涉及到两次直角三角形计算,大致示意图如下:

泰课在线

    现在开始动笔计算吧,在立体空间中我们如果忽略高度,那么红蓝这两个点就会形成在一个平面上。在这个平面上的点就成了一个虚拟的点(绿点)。而这个绿蓝两点的特点是除了高度之外其它坐标值相同,因此我们第一步先计算红绿之间的距离。如果红点为 {x1,y1,z1},蓝点为{x2,y2,z2}那么计算红绿的公式就是:

泰课在线

    接下来我们开始在奶酪斜面上计算斜边边长。还是直角三角形计算公式:

泰课在线

    下面我们把两个公式合并起来,顺便做一下简化。

泰课在线

    现在我们可以写一个函数来计算长度了。

七、别高兴的太早!

    到现在现在似乎所有问题都解决了,我们在回顾一下 InBox 现在的模样:把本地消息发送到专有的 InBox 里然后在转发的时候进行 Filter 计算距离做一下判断,现在应该是这个逻辑。

泰课在线

    看上去似乎完美了。现在假定:如果有 1000 个玩家同时在线,其中一个人说了一句话,那么这个人的这次发言就要把所有在线玩家的距离都计算一遍,这是1000次计算。那么如果这1000个人在世界里都说了一句话就要计算 1000 * 1000 一共 100W 次的计算。

    如果说话的人频繁说话呢? 200W次计算? 500W 次计算?还是 1000W次计算?, 倘若是 3000人在线呢? 或者万人在线呢? 

    话说我们可以控制一个人的说话频率,但是这个计算量是指数级的增长。单独控制一个变量是没有用的,所以我们还是要从发送消息的角度去控制。毕竟我们不能控制整个世界的聊天频率,这不科学。

八、用空间换时间!

    前面提到的方案里最大的问题就是计算量的问题,那么有没有一种方式可以避免大量计算呢? 

    我们先假定地图是二维平面的,所有人都在这个二维平面网络上。然后我们把大的地图网络划分成许多个方块,根据玩家坐标都可以归类到某一个格子里去,例如下面左边的图:

泰课在线

    接下来,我们为每个格子打上 ID 并为这个ID创建一个专属的地图频道。这样一来地图上的所有位置都对应的有一个唯一的地图区域聊天频道。

    现在只要把玩家发出的聊天消息发送到特定格子的频道里就OK了,其他玩家只管接收他所处的地图频道內聊天信息就可以了。

    OK,现在剩下的问题就是如何把玩家放到格子里的事情。这样一来我们就不需要再去遍历全部玩家列表,也不需要去计算玩家距离,只需要往特定的频道里发消息就ok。

九、画格子

    首先我们知道游戏地图都有地图的坐标系统,在3D游戏里表示一个游戏内玩家坐标可以用 x,y,z 三个量来表示。而我们的聊天频道是根据二维平面来计算的,那么如果忽略玩家所处的高度。我们的3维坐标也就变成了我们需要的二维坐标。

    另外一个问题,如果我们为地图上每一个坐标都创建一个频道,那玩家和玩家之间只有亲密站在一个点的时候才能收到彼此的消息。这个也不是我们所看到的。因此我们需要把 100 * 100 的地图坐标映射到 3 * 3 的频道网格里。

泰课在线

    现在我们假定玩家位于蓝色地图区块内说话,他的地图坐标相当于 5,5 ,聊天频道区块的坐标相当于 2,2。

     我们需要一个函数,让用户坐标经过这个函数的时候变成 2,2。 这里可以把两个二维网络当作完全相同的两个地图,只是比例尺不太一样。那么我们只需要找出两张地图的比例关系。在把玩家的实际 坐标和比例关系相乘一下问题就结了。

    根据这个 case 我们知道,消息格子和地图之间: x 轴的比例关系是  3:9 = 1:3 , y 轴上比例关系也是 3:9 = 1:3 。

    这样就可以得到这样一个近似的转换坐标。 x: 0.33,y=0.33。接下来玩家的实际坐标  5,5 乘以转换坐标 0.33,0.33 得到的就得到 x =1.65 , y= 1.65。

    然后再把这个结果做 “Math.ceil”的取整,最后得到了格子坐标 2,2。接下来就把消息发到这个格子的频道去就好了,其他在这个频道的玩家都会收到消息。哪怕有 100W 人在线,也不需要去计算任何距离。CPU 效率用到最大化。

9.1 - 3D下的格子

    3D下画格子看似很玄乎其实就是一个两个魔方套在一起,道理是一样的。计算方式也是一样的。甚至通过坐标转换两个格子的函数也是一样的。

泰课在线

    例如:消息格子和地图之间: x 轴的比例关系是  3:9 = 1:3 , y 轴上比例关系也是 3:9 = 1:3 , z 轴上比例关系也是 3:9 = 1:3 。

    这样就可以得到这样一个近似的转换坐标。 x: 0.33,y=0.33,z=0.33

    接下来玩家的实际坐标  5,5,5 乘以转换坐标 0.33,0.33,0.33 得到的就得到 x =1.65 , y= 1.65,z=1.65。

    结果做 “Math.ceil”的取整,最后得到了格子坐标 2,2,2。 这个格子就是魔方的正中心,如果玩家 z 坐标小一点到 2,那么坐标计算之后 z 格子 就是 2 * 0.33 = 0.66 ,取 Math.ceil 计算后得 1,这样玩家就跑到盒子的上面去了。

9.2 - 真的就解决问题了么?

    好盒子画完了。现在让我们在微观上,重新审视一下本地聊天系统是否还存在其它问题。

    如下图所示,现在有三个玩家,根据他们实际的地图坐标已经把他们划分好具体的格子。我们发现:红蓝,两个玩家虽然距离上很接近但是处于不同的格子。这个会造成蓝色玩家把消息发送到左边的格子里,那么红色的玩家就不会在右边收到他的聊天消息。即便是他们两个如此近距离,这也不是我们希望看到的。

    再来看,蓝色和紫色玩家因为在一个频道内即使距离很远,紫色玩家依然可以收到,我们可以认为格子的斜边长就是紫色玩家听力的极限范围。这么去解释也说得通。但是绿色玩家和蓝色玩家之间的距离明显比蓝色和紫色的要近,收不到消息有点说不通了。

泰课在线

    好吧,为了解决这个问题我们还要解决发消息发往哪些格子,至少目前开看还要把消息发往周边的格子。为了方便理解让我们开始继续画格子把:

泰课在线

    这样一来,就解决了我们遇到的蓝色玩家发的消息,紫色玩家收不到的情况。 3D 情况下,大家可以自行脑补一下 3 * 3 * 3 的格子方阵,道理是一样的。

9.3 - 处理玩家移动

    玩家不是一直就站在原地的,玩家会动。这也就说明,我们给玩家圈定好的聊天频道会随着玩家的移动而发生变化。所以地图频道还要有进入频道、离开频道这样的基本功能。当玩家进入一些格子之后就必然要离开一些格子。

泰课在线

9.4 - 还可以更完美么?

    我们说做事情要有匠心精神,现在看样子我们已经完美的解决了本地聊天的问题。现在我们在重新审视一下我们的设计是否还有其它漏洞。

    我们知道声音是按直线传播的,在传播中会逐渐衰减。假定一个人正在讲话,从能听到他讲话到听不到他讲话的距离是20米,那么两个人听力完全相同的人,理论上无论他们在什么位置只要以讲话人为中心,20米的范围内都可以听到讲话。一旦超过这个距离就应该听不到。

    我们画一个模型来表示这个意思,同时对比一下我们的格子模型。为了说明问题,我把发送范围扩大到 7 * 7 的范围。

泰课在线

    大家看到了把,在真实环境中的人(绿点)在我们的格子模型中居然收到了消息!

    为了让系统完美一点我们继续画圆(如上面右图)首先我们知道消息的发送最大半径为 3 个单位,那么算上消息发送者自身的起点为圆心,整个半径应该是 4个单位,直径是 7个单位。接下来我们要找到这个圆圈所在的正方形四个顶点。

    假定消息发送的格子坐标点为 {x= 25,y =20} ,那么最大喊话半径为 3 ,可以得出一个矩形对角线坐标为 {x-3, y-3}{x+3,y+3}。接下来只需要循环这个矩形的每个点,都和中心点做一个距离的计算然后排除掉超过半径的格子就会得出我们最后希望收到消息的格子。

    如果大家觉得 7 * 7 的格子的圆太过粗糙,可以提升半径数以增加圆的光滑度。举个例子:喊话半径为 100 个单位,那么每次喊话的计算量在 w=201,h=201,4.04W。

    我们来直观的看一下  50 半径的光滑度是个什么样子的,图中每个红色格子里面都是一个 10 * 10 个小格子组成。

泰课在线

    在看看 半径 100 单位的格子光滑度(为了方便,使用一张 201 * 201 像素的图片作为代表),怎么样够可以了吧。

泰课在线

    实际上需要这么细腻么?我们 50 半径就已经很给力了。

9.5 - 追求极致性能!

    到目前为止我们定义的格子模型和地图坐标系统之间是静态的关系。格子和格子之间的关系始终是静态的,玩家移进移出格子不影响格子与格子之间的关系。那么格子的关系既然是静态的,那么整个世界上如果都遵循这一个定律(消息最大传播半径 100),是不是就可以预先把每个格子都算一遍。这样玩家在发消息的时候就可以避免 4.04 万次的计算。然后把每个格子的计算结果都作为格子的一个属性预先保存在内存里。

    如果这个配置是整个服务器的基础配置,我们甚至可以把计算的结果保存到磁盘上。这样每次服务器启动加载格子数据的时候,都不需要做计算了。

    效率最大化,这正是我们希望看到的!

    OK 现在我们看样子已经解决了所有问题,玩家发送消息的时候玩家在地图上任意位置发送消息给所在地图频道以及周边的频道。这样周边的玩家就可以收到聊天消息。现在在回头看看我们的 InBox 的模样,还是那么简洁。

泰课在线

十、格子画多少比较合适?

    画多少格子说白了就是在考虑系统承载力的上限多高,因此我们要尽量把数字合理的夸大一些。然后评估出系统能力的边界,这就好比淘宝每年做双十一系统压测,都是把数字夸大好几个量级。让它成为一个看似不可能达到的上限。

    下面我们就开始评估我们的系统设计上限,通过“假定-> 计算 -> 调整”。我们可以得到一个比较靠谱的数值,现在就让我们先从最不靠谱开始吧。

10.1 - 地图大小

    现在开始,我们来讨论一下画多少个格子比较合适这个问题。在讨论这个问题之前我们先看看全局地图到底该多大,在此之前我们用真实世界的 米 作为计量单位。

    先假定一个游戏有一张很大很大的地图,按照玩家每秒 1 米的移动速度。假定玩家从最东边跑到最西边需要24 小时,那么游戏的地图应该有 86,400 米。

    如果这个数值换算成游戏地图面积应该是 7464.96 平方公里。

    游戏设计者在这么大的面积上,可以把一座巨大的山峰设计成 100 米,长江的宽度设计成 10 米。总之把你的地形数据适当的缩小,那么这个大地图可以表示你想要的一切。而且它的单位面积也足够大。

    在真实游戏环境地图坐标上我们可以采用 小数 来表达以扩大地图精度。7000多平方公里的地图资源,应该是足够用了。当然不够的话可以在加!

10.2 - 消息格子数目

    格子尺度越小,格子数越大,假如一平米一个格子,那么场地面积就是格子数。可以写作:n=(x / a) * ( y / a) ;n = xy / a ^2

    (n:最总格子数,x:x轴地图尺度,y:y轴地图尺度,a:格子划分尺度)

    我们假定 x=y,那么(n = xy / a ^2,变为:n= x ^2 / a ^2 ),先看一下 1:1 关系下格子数的曲线。可以看到 x 轴我们的地图尺度不断增加,格子数的增长非常快。

泰课在线

函数曲线生成地址:http://www.archimy.com/ ,下面是脚本:

z = 0
xgrid = 86400
a= 1
y = (x ^2) / a ^ 2

如果比例单位小一点呢?我们调整比例关系 a ,来重新对比格子数增长情况:

泰课在线

    为了确定我们的演算结果,我们按照上面的比例系数算一下 86400 米见方的地图上格子数是怎样的:

    1米单位:7464960000 个格子[86400 * 86400];
    3米单位:  829440000 个格子[(86400 / 3  ) * (86400 / 3  )];
    10米单位:   74649600 个格子[(86400 / 10 ) * (86400 / 10 )];
    20米单位:   18662400 个格子[(86400 / 20 ) * (86400 / 20 )];
    50米单位:     2985984 个格子[(86400 / 50 ) * (86400 / 50 )];

10.3 - 格子占用内存空间

    现在我们计算一下,要想把这些数量级的数字记录到内存里究竟需要多大。

    7464960000,这个数已经进入 long 的范畴,我们需要用 8 个字节进行存放,要想存下这么多的 8个字节数据我们需要 55.61G 的空间去存储。显然这是非常非常不靠谱的,直接放弃。

    829440000,还在 int 的范畴,可以用 4 个字节来存储。这个数我们也算一下,大约需要有 3.08GB 的数据存储空间。还是太大,也直接放弃。

    74649600,这个数我们也算一下,大约有 284.76MB 的数据。这个数量还可以接受。

    18662400,这个数我们也算一下,大约有 71.19MB 的数据。这个数量不错。

    2985984,这个数我们也算一下,大约有 11.39MB 的数据。这个数也很不错。

10.4 - 与周边格子关系数和存储

    那么已玩家为中心,玩家说一句话所影响到的格子数我们通过圆的面积来粗略计算。如果一公里地图长度,按照 10 米单位为切分的话,整个地图的宽度就会缩减到 100,100为直径的圆圈内粗略算一下面就可以得到格子数。废话少说上公式:

泰课在线

n:为我们要求出的某个格子周边关系的近似数。
a:实际单位:米
b:划分格子的度量,即多少米为一个格子。

    公式有了,我们现在假定玩家具有河东狮吼的能力,每次说话会让周边 3 公里的人都听到,同时我们使用 10米 这个单位。那么带入公式得到:

n= 3.1415 * (3000 / 10)^ 2 ,约等于 282735,就是说在 10米 为一个格子的情况下,玩家说一句话让周边三公里内人都听到。需要当前玩家所处的格子与周边约 28.2W 的格子进行关联。同时如果每个格子都要这样做关联那么将会产生巨大的关联关系(相当于74649600 * 28.2W),这个数值必须被否掉。

    还是两条路,1.降低格子数;2.降低消息范围。

    在这个case中,消息传播距离 3 公里实在是有点太夸张,那么我们降低到 1 公里,或者在降低到 500米?下面直接上结果:

1公里:31415 个关联格子,约3W;
500米:7853.75个关联格子,7800多个;

    这个数值很不错!如果每个格子都预先把周边 1 公里的格子做好关联,那么就会是:74649600 * 31415。为了降低存储空间,31415 使用 两个字节的 short 表示。这些关系一共需要 74649600 * 31415 *2 个字节,约为:4368.12GB 这绝对不可能让它发生。

    好吧,试一下 500 范围的:74649600 * 7853.75 *2,约为:1092.03GB,这也太高了。看样子降低范围已经不能解决问题了,我们需要降低格子密度!

    使用 20米密度再算一遍关联格子数(18662400格子数):

3公里 (n= 3.1415 * (3000 / 20)^ 2 )   70683.75 个关联格子,约7W;
1公里 (n= 3.1415 * (1000 / 20)^ 2 )      7853.75 个关联格子;
500米(n= 3.1415 * (500 / 20)^ 2 )       1963.43 个关联格子;

    我们在重新计算一下所有关系需要的存储空间:

3公里 (18662400 * 70683.75 * 4)约:4914GB,无法直视。直接放弃(乘 4 是因为 70683 这个数需要 4个字节来存储);
1公里 (18662400 * 7853.75 * 2)约:273GB;
500米(18662400 * 1963.43 * 2)约:68.2G;

    看样子 20 米单位的格子密度还是太高,现在只有选择 50 米密度了(2985984)。再算一遍,这次直接给出结果:

关联格子数:
    3公里(n= 3.1415 * (3000 / 50)^ 2 )   11,309.4 个关联格子,约1.1W;
    1公里(n= 3.1415 * (1000 / 50)^ 2 )     1,256.6 个关联格子;
    500米(n= 3.1415 * (500 / 50)^ 2 )       314.15 个关联格子;

存储大小:
    3公里 (2985984 * 11,309.4 * 2)约:62.9GB;
    1公里 (2985984 * 1,256.6 * 2)约:6.98GB;
    500米(2985984 * 314.15 * 2)约:1.74GB;

10.5 - 不建议划分3D格子

    前面我们讨论的数据都是 2D 地图上的格子数据,假如你觉得。在30层楼的人不应该听到 1 楼人的窃窃私语,那么解决办法有两个。

    1. 缩小消息传播尺度,同时或者增大格子单位。
    2. 不使用3D的格子模型,在格子转发消息的时候。对接受消息方的 z 轴做数值判断。因为计算量小可以“时间换空间”。

    先说第一种方法,为了找出计算机可以接受的格子数。将格子单位从 20米 提升到 100米,我们的地图模型假定是一个立方体。因此格子数等于:(86400 / 100 )  ^ 3 =644,972,544, 6.44 亿个格子。6亿个格子 ID 用 4字节 int 来存需要用到约 2.4GB 空间。

    同时我们降低消息传播范围到 100米,利用球体积公式(4/3 * 圆周率 * 半径的立方)得到每个格子周边可能有关系的格子数近似值约:4.18

    注意:这里因为我们传播半径为100米,格子半径也是100米。按照常理来说这样的传播范围应该是一个 3 * 3 * 3的方格,因为距离实在太近了,以至于我们无法用常理的球体积公式来进行计算。

    可就是即便采用球体积公式的结果 4.18 我们也发现,6.44 亿个格子每个格子都有约 4.18 个临近对象的话。光是存储空间就需要 2.4 * 4.18 GB 约 10GB 的空间,再加上个格子ID的存储空间,大概需要 12G 的空间!!!

    因此我更建议使用第二种方法实现立体范围的消息传递,因为在游戏中同一个格子上垂直方向上不通高度密集分布玩家的情况不太可能发生。而画立体格子的代价实在是太高昂了。

10.6 - 回顾我们选择的参数

    看样子,我们找到一个适合的参数了。我们要在方圆 7,464.96 平方公里的土地上,画出大约 298.6W 个消息格子,平均每个格子需要记住周边 314.15 个临近的格子ID。这样的配置下,我们需要最小 1.74G 左右的内存,其中地图格子数据 11.39MB

    这个配置看上去还可以,就先用这套配置吧。当然如果你想,可以自行调节(地图尺寸、格子比例、传播半径)这三个参数。调参数的手法前面已经演示的非常清楚了。

十一、看看具体性能如何?

    玩家在登陆游戏服务器之后。第一步,服务器根据他的游戏坐标,找到他的消息盒子。这个操作只需要用玩家的坐标乘以比例尺(0.2)在做一个 “Math.ceil”即可得到格子坐标。(产生 1 次计算)

    每个玩家都有一组状态数据,玩家初次登陆的时候根据坐标,可以预先算出,玩家像四个方向移动多少米之后会触发格子更新事件。接下来玩家每次移动都只要把移动消息发出来,交给这部分逻辑处理器去处理就好了。逻辑处理器负责计算是否要触发重新注册格子。(每次移动都会产生 1 次计算)

    进出格子的计算量,每个格子都有大约 314.15 个临近链接。每次一旦决定游戏玩家从一个格子跨遇到另外一个格子之后,需要比对两个格子之间差异的,最少需要做 314.15 * 314.15 次计算和判断,共计约 98690 次计算。好在这个计算量并不是每次玩家移动都会触发,不然这个计算量也不容忽视的一个问题。

    触发玩家更新消息格子的条件是,当游戏玩家离开所处消息格子时。这样一来玩家只要在我们单位尺度内,随意移动都不会造成玩家移动触发 9W 次的判断计算。例如下图:

    现在列举两个极端问题,看看系统会发生什么。假定单个大区服务器设定上线玩家数为:10W人。

    1. 当所有玩家都聚集到一个格子里,并每个人都说一句话。那么会发生什么问题?

    首先 10W 人都在一个格子里,意味着一个人说话会被 10W 个人同时听到。消息的转发次数是 10W 次,每个人都说一句话,消息的转发次数是 10W  * 10W。

    说实话,这种极端情况下我们也没什么可以做的。因为消息毕竟还是要发到每个人手里,我们能做的是尽量控制玩家过于密集的在一个区域内。况且 10W 个人物对象同时渲染在一个屏幕里,恐怕显卡也造就烧爆了。所以理论上不太可能发生。

    说到这个 case 我到想起早期 魔兽世界 屠城的时候,20个 40人团队,直接冲进联盟暴风城的场面。别说屠城了,去的人没有一个人能在当天出来,整个暴风城及其附近的几张地图全部遭遇服务器宕机。

    2. 所有玩家平均分布在两个格子之间的临界点,然后开始匀速的左右移动。这回造成服务器为每个玩家频繁计算 9W 次的进出格子判断。

    这也是一个不容小视的问题,解决这个问题也有很多办法。为了简单可用,我们设计一个专门用于保存玩家状态的服务器集群,20台 机器每台机器管理 5000 个在线用户,9W 次的计算会按照用户为维度,分不到不通的server上。问题解决合理解决。

话有点多,喜欢多关注“泰斗社区”还有更多相关话讲~