本文主要聚焦于多人在线游戏的三个技术难点:
1.通信架构的选择
2.服务器的承载能力
3.玩家体验

通信架构的选择

同步方式和异步方式

  • 同步: 同步简单粗暴,数据稳定性高,但需要收集到所有玩家数据后才能做出下一步反应,因此所有玩家的体验取决于网速最慢的那个人
  • 异步: 异步方式体验最好,可以容忍更加不稳定的传输线路和糟糕的延迟,但需要处理很多异常状况

游戏中具体采用哪种通信架构并没有一个标准答案,要根据游戏具体的设计和玩法来进行对应的选择

接下来我们就对游戏世界的三种典型情况进行具体分析:

构成游戏世界的三大基本要素

  • 玩家和npc的交互(异步)
    例如格斗游戏,对即时响应的要求非常高,攻击方需要立即看到攻击结果,防守方也需要立即看到防守的结果,因此需要采用异步的通信方式
    但是这样就势必会导致攻击与防守的异步冲突,目前行业内普遍采用如下解决方案:

    如果是不利的结果,采用承受方的结果,如果是有利的结果,采用施与方的结果(或者根据双方数据到达中央服务器的时序进行判断)

  • 玩家和环境的交互(同步)
    拿炸弹举例,玩家A和玩家B同时走到炸弹面前进行拾取,假设玩家A在捡到炸弹后立即使用的炸弹,炸死了玩家B,甚至在地上炸出一个大坑,但后续服务器判断玩家A并没有捡到炸弹,而是玩家B捡到了,这时候就会出现一个很复杂的难题:重新复活玩家B?把地上炸出的坑重新填回去?这样就会有无数的不可预期的情况需要去做回档处理,因此采用异步会有很大的不一致问题,所以这种情况应采用同步的方式判断炸弹是否已经被别人捡走,同时播放一个捡到东西的动画,在动画期间去服务器同步数据

    除了炸弹,还有门,开关等环境因素需要采用同步的方式

  • npc和环境的交互(异步)
    大多数情况下,玩家并不关心npc与环境的交互,可以选择性的进行通信及渲染,同时可以降低通信的频率,因此可以采用异步通信方式

同步方式的优化

  • 上文提到,同步方式的痛点在于网速最慢的哪个人,那如何处理网速不好的那个人就成为了优化的核心,目前比较成熟的解决方案是不用等待所有玩家的数据都收集到位再进行广播,而是采用定时广播的策略,到了一定的时间周期,就把该时间点收取到的信息广播出去,忽略网速慢的人,此方案的优点是网速好的人的体验就会很好,缺点是网速不好的人的体验就会变糟)

服务器承载能力


拿上图的mmorgp举例,在遇到游戏活动期间有可能会出现满屏挤满玩家的热闹情况,这对于游戏开发商或运营商来说肯定是好事,但对于技术同学来说就不一定了,如果我们还是简单粗暴的把每个玩家的状态数据实时的广播给同屏的其它玩家,那每一逻辑帧都会发生n*n次的广播次数,例如同屏有100人,则会出现每逻辑帧10000次广播,这对于服务器来说是种巨大的压力,客户端也不会得到良好的即时响应,因此我们需要一些策略确保大多数玩家的良好体验,以便玩家能在游戏中愉快的游戏下去.

数据广播策略

mmorpg:

数据广播算法:

  • 九宫格法
    九宫格也是打格子的方式之一,把地图划分为很多小格子,每个格子记录格子内的玩家,每个玩家的aoi范围是以自己为中心范围内的九个格子,九个格子的大小略大于屏幕大小,同样的有三个主要的操作:enter,move,leave
    enter:根据玩家坐标,加入到所属的格子中,通过计算以这个格子的为中心的九个格子,这九个格子内的玩家就要被通知有新玩家初始化,同时这个新玩家初始化九个格子内的所有玩家。
    move:根据移动前位置的格子,计算出移动前的oldaoi集合,根据当前位置的格子,计算出当前的curaoi集合,如果oldaoi, curaoi为同一个格子,则通知格子内的所有玩家该玩家在移动。如果oldaoi,curaoi不是同一个格子,即发生了跨格子的操作,那么要将该玩家从旧格子移除,同时加入新格子。同时分别遍历oldaoi,curaoi,计算出需要通知玩家消失的格子集合,通知玩家出生的格子集合,以及通知玩家移动的格子集合。
    leave:玩家离开地图,将玩家从对应的格子里面删除,同时通知aoi集合有玩家离开。
  • 灯塔法
    灯塔法,是九宫格法的改进版,将大地图划分成有限的小格子,在每个小格子中间放一个灯塔,这个灯塔管理两个队列:一个是本格子内所有的对象集合,另一个是对本灯塔感兴趣的对象集合(简称观察者)。
    而地图上的每个对象,维护一个视野队列:该队列为其视野范围内的所有对象,即自身感兴趣的所有对象。
    一个对象在地图上面运动:分为三个操作:enter,move,leave.
    enter:当对象进入地图的时候,根据对象的当前位置和对象的感知距离,可以获取到该对象能观察到的所有灯塔,遍历这些灯塔,将该对象添加为其观察者。同时将这些对象添加到自己的视野队列中。
    move:当对象开始移动的时候,对象从一个点到另一个店,那么视野范围必然发生变化。此刻需要将对象从老的灯塔的观察者列表移除,同时将对象添加进新的灯塔的观察者列表。此外,还需要跟新玩家的视野队列,因为视野范围变化,视野内的对象也相应变化。
    leave:当对象离开的时候,将自身从附近灯塔的观察者队列中移除。
    通过灯塔法,每当物体发生变化,我们能马上根据其当前位置,定位到他的所在的灯塔,同时找到它视野范围内相关联的物体。这样避免了遍历地图上所有玩家进行处理的方式。
    当然灯塔的格子大小划分要因地制宜,格子越小,消耗内存越大,同时计算量变大。
  • 十字链表法
    这里以2d游戏为例,3d游戏顺势扩展即可。
    所谓十字链表法,即维护两天链表,一条根据地图上所有物体的x坐标从小到大依次插入链表,一条根据地图上所有物体的y坐标从小到大依次插入链表,可以想象成一个十字架。这样便把地图上的所有对象按序分配到了x,y链表上。
    这里的链表为双向链表,双向链表的好处是,获取到链表中的一个节点,便可以向前和向后遍历。这样,当我们拿到一个对象时,要获取该对象的视野范围就变得非常简单。避免了从头到尾遍历所有对象。
    首先根据x坐标,在x链表上找到该节点,然后从该节点向前和向后遍历,根据x方向的视野范围找出需要识别的对象。
    然后根据y坐标,在y链表上找到该节点,然后从该节点向前和向后遍历,根据y方向的视野范围找出需要识别的对象。
    拿到x,y链表上需要关注的对象,然后取他们的交集,这便是玩家视野范围内的对象。
    对于对象在地图上的enter,move,leave 。根据前面的思路就变得非常简单

根据地图切分数据

  • 服务器只通知同一区域的玩家
    游戏在设计时需要考虑服务器承载量的问题,按承载量切分为不同的区域,服务器只需要在同一区域内广播消息,而不需要在所有区域进行广播
  • 根据服务器的承载容量来设计客户端区域
    大多数时候游戏内的区域是按照客户端的设计来作为区域划分的(考虑内存控制,帧率,关卡逻辑等),但必要时也需要把服务器的承载能力考虑进去,即便在某个区域客户端是不需要切场景的,但由于服务器的原因,仍然需要人为的加上一个过场动画作为服务器切区域的缓冲
  • 根据服务器承载容量动态分流玩家
    在某些热点区域,比如节日活动,运营活动等可能导致玩家在某一区域大规模聚集的情况,可以提前准备好几个服务器副本,再加一个副本网关,在玩家大规模流入时根据每个副本的承载容量动态的分流玩家,避免出现大量玩家在某个单一的副本聚集的情况发生

根据可视范围同步数据

  • 考虑墙的因素


服务器广播时,可以根据地形只通知在某个可见区域内的玩家,而不需要通知墙另一边的玩家,以减少广播压力.

根据视角同步数据

  • 视锥


服务器可以根据玩家的可视范围(视锥)进行广播,不需要考虑视锥以外的其它玩家.

  • 视锥改进方案

    但上述方案有个缺点,如果玩家很快的转身,那些虽然在身边但不在视锥范围内的玩家就会因为没有相互同步数据而导致错乱,因此可以用视锥+圆(以自身为圆心,一定距离为半径的)的改进方案来规避这个问题

声音元素的特殊处理

  • 声音元素需要特殊处理,有些元素并不在可视范围内,但却是不可忽略的关键因素,比如手榴弹这种会带来一定范围伤害的特殊元素,它如果发生爆炸则必须在一定范围内通知到周围的玩家,因为它很可能对游戏接下来的发展产生关键影响,不能因为它在转角的不可见处爆炸的就让玩家毫无感知,这种特殊元素的计算复杂度并不高,只需要以事件触发点为圆心,一定通知距离为半径进行广播就行了

复杂环境处理

如果判断环境的计算量超过了广播本身,则需要进行权衡

在一局100 vs 100的多人游戏中,最开始的复杂度一定是最高的,需要用到上述的各种方法进行过滤,以优化服务器的传输数据量,但到了游戏后期,整个游戏只剩10几个人甚至更少的时候,就没有必要再用上述方法进行过滤判断了,因为这些判断的计算量可能会大于全体剩余玩家全部参与广播的计算量

上面介绍了比较粗的优化服务器广播数据量的方案,接下来我们来扣扣细节,那些绕不过去必须要进行数据同步的单位,有没有更进一步的方法减少通信的数据量:

最小化数据传输优化策略

角色移动同步

• 对关卡地形进行格子划分,计算量的大小取决于所划分格子的尺寸,尺寸越小计算量越大
• 只对服务器同步跨格子行为,不同步每次的动作
• 同步跨格子时一并同步精确的坐标,尽量提高计算精度和准确性

怪物移动同步

• 怪物原地巡逻的逻辑由客户端自己来控制,不用同步给服务器
• 玩家攻击怪物设定为扇形攻击,避免每个客户端自行计算的位置不同所带来的误差
• 小boss:怪物AI行为由客户端接管,接管方式是异步抢占式,优先和服务器完成通信的客户端抢占到怪物AI的控制权,一旦有客户端获得控制权则广播给其余的相关客户端,通信数据不用同步每次攻击事件,只通知服务器关键事件的开始和结束,比如怪物开始攻击主角,怪物被主角杀死,主角被怪物杀死等等
• 大boss:比较重要,因此由服务器进行第一步逻辑运算,选择出要攻击的客户端(同步),然后由客户端接管AI,同步给其它客户端(和小boss的处理方式一致)

玩家频繁操作的解决方案

• 缓存池+定时发送,对玩家操作进行缓存,在一定时间间隔内用新的操作覆盖旧的操作,到达一定时间后发送最新的操作给服务器,这样可以避免玩家条件反射般疯狂点击鼠标所带来的无效数据同步

玩家体验

如何应对延迟

  • 延迟对于部分类型的游戏是致命的

  • 延迟是怎么产生的

    假设客户端发送的消息达到服务器需要经历100ms,服务器返回给客户端的消息也需要100ms,两者相加得到的总时间就是200ms,这就是从客户端发送消息到服务器并得到回应的总延迟为200ms

  • 我们看到的太阳是八分钟前的太阳

人的视力和听力对时间的容忍度

  • 如果能预想接下来可能的运动情况:50毫秒

    举例:赛车游戏中,在过弯时,我们对对手的操作是有一个基本预期的,比如过弯时对手大致会做什么样的操作,车的运行轨迹在我们的潜意识里会存在一个基本的轨迹,如果对手没有按照这个基本轨迹进行移动,并且在50ms内都没有修正为"正确的轨迹",那我们就会发觉不自然
  • 如果是自己操作的行为或动作:10毫秒

    举例:格斗游戏中,我们按下了"下+脚",我们的潜意识预期将军会做出滑腿的动作,如果超过10ms都没做出这个动作,我们就会感受到不自然,会觉得迟钝

不同类型的游戏延迟容忍度

  • 格斗游戏:25毫秒
  • fps和战略游戏:100毫秒
  • mmorpg:300毫秒

典型案例分析—FPS射击游戏


举例:经典的CS游戏,对延迟的容忍度非常的低,如果不在技术上进行适当的处理,联网的射击游戏几乎都是没法正常玩的,接下来我们就来分析为什么没法玩,以及解决方案.

  • 假设,网络延迟为单边100ms,敌人从左到右做匀速直线运动
  • 当玩家瞄准一个敌人(位置1)并扣动扳机进行射击时,按照上述讨论过的"延迟是怎么产生的","我们看到的太阳是八分钟前的太阳"模型,实际敌人的位置已发生了变化(位置2,在玩家看到敌人的右边),玩家看到的是敌人曾经所在的历史位置,在射击的瞬间,敌人已不在玩家所看到的位置
  • 玩家扣动扳机的事件发送给服务器,当信息传递到服务器时,敌人的实际位置又往右移动了一截(位置3)

可以说,客户端看到的永远是敌人的错误位置,在错误的位置上进行了抉择和判断,再加上网络延迟,更是错上加错,那还怎么搞,射击游戏岂不是没法做了?

其实针对这个问题,早已有了成熟的解决方案:

服务器回退法
  • 游戏引入逻辑帧的概念,客户端和服务器根据逻辑帧进行一致性判断(引入逻辑帧并不代表要做帧同步,这是两个完全不同的概念,不要混淆)
  • 服务器每时每刻都记录过去n个逻辑帧的数据(这里假设n=5)
  • 客户端的所有消息都需要带上当前所处的逻辑帧
  • 服务器根据客户端标记的逻辑帧进行回退
    举例:假设客户端在第577帧逻辑帧进行了射击动作,当事件到达服务器时,逻辑帧已经运行到582帧,服务器接收到客户端消息后,发现客户端标记的逻辑帧是577帧,则取出之前保存的577时,敌人所在的位置,接下来是否命中的判断就如同单机游戏了.
  • 这里的例子只涉及到位移,真实游戏中还会涉及敌人的状态,buff等多种因素,都可以采用服务器回退法进行复原
  • 由于联网射击游戏这种类型的游戏参与人数并不多,不像mmorpg那样动则上百人,因此即便需要缓存n帧的历史帧数据,其数据量仍然是很小的,不会造成瓶颈
  • 大部分对网络延迟有高要求的游戏类型都适用于服务器回退法进行处理
  • n的取值取决于具体的游戏类型,比如fps游戏n<=5, 格斗游戏 n<=2
  • n取值不能过大,否则会造成玩家的负面体验,比如上面的cs例子,假设n=20,则会出现敌方玩家已经离开门很远了,确仍然莫名其妙被站在门前的另一个玩家击中的奇怪体验

如何从架构层面减少延迟?

  • 低延时系统方案,tcp vs udp

udp=不可靠?

大多数时候我们看到udp出现的地方都伴随着"不可靠"这三个字,于是我们都对udp避而远之,尽量不使用,通过上表我们可以看出,与其说是不可靠,不如说upd较难把控,不像tcp很多东西都封装好了,开箱即用,udp一旦没处理好就可能造成bug,但真正想从系统层面降低延迟,必须要熟练掌握udp,热门的王者荣耀就是使用udp降低网络延迟的优秀代表作

如何利用udp,让其做到低延迟和可靠性?

  • 对消息进行分类和编号,不是所有消息都是一样的,我们可以根据需要灵活进行操控和处理
  • 单向消息,不需要服务器回应的消息,发送后也不需要等待返回,相当于减少了一般的延迟
  • 可被大编号覆盖的消息,像玩家鼠标点击操作这种消息,可以用大编号的消息覆盖小编号的消息,从而忽略掉部分已过期的消息的处理,降低客户端的等待时间
  • 要求回应的消息,类似于传统的tcp消息,必须要求服务器进行响应

客户端的本地体验优化

  • 客户端预测
    不管怎么降低延迟,延迟只能尽量减少,但始终是存在的,不可能被优化为0,因此在等待服务器返回数据这段时间,客户端可以进行一些优化手段,避免出现糟糕的游戏体验,比如mmorpg中,客户端可以根据其它玩家上一次的操作逻辑进行预测,比如上一次是做左边往右边移动了10个像素,那我们可以预测其接下来也会继续往右移动10个像素,而不是让该玩家停下来,等待接收到服务器数据后再进行移动,这样该玩家看起来就不会很奇怪的走走停停.

客户端预测根据游戏功能的不同可能会涉及到各种不同的AI技术,以便让其它玩家看起来更自然,这里涉及的技术很多,这里不再过多描述

  • 根据服务器数据进行纠错
    客户端根据多种方法进行预测后,很可能会出现预测结果与真实行为不一致的情况,因此,在延迟从服务器获取到真实数据后需要对现状进行一次纠错.
    • 瞬时状态更新
    用服务器数据强制重置一次客户端数据,优点是能立即恢复到最正确的状态,缺点是会频繁出现瞬移,跳帧等奇怪的表现
    • 平滑插值
    让现状在设定时间内根据服务器数据平滑插值变化过去,优点是改变有一个过程,不突兀,不会出现瞬移等表现,缺点是对一些即时信息要求较高的游戏类型会有些拖沓,导致游戏体验下降
    • 二阶状态调整
    是平滑插值的一种优化方案,采用缓-快-缓的二阶节奏进行平滑插值,优点是比平滑插值更为自然,缺点和平滑插值一致

tips:小误差平滑插值,大误差即时状态更新

  • 客户端加入过渡动画来掩盖发送到服务器&接收到服务器数据这段时间的延迟
    客户端可采用一些小技巧来避免生硬的等待服务器返回数据,比如弓箭手在射箭时,播放一段拉弓的前摇动画,在播放动画这段时间大概率已经收到服务器回发的数据了,这时就能让玩家最大程度上消除对延迟的感知.

皇室战争在放兵时并不是立即就能把兵放到地图上去,而是会播放一个时钟的动画,这段动画的持续时间是动态变化的,时间大概有1~3s,其实这段时间就是在等待服务器返回数据,这种优化方案并没有对游戏本身带来不良的影响.

参考资料:
<<网络游戏核心技术与实战>> 中嶋谦互
<<网络多人游戏架构与编程>> Joshua Glazer,Sanjay Madhav
Unity手游实战:从0开始SLG——世界地图篇
一款已上市MMO手游地图同步方案总结
解码mmo游戏服务器三:大地图同步(aoi)
MMORPG大世界服务器技术浅探(1)——服务器框架

多人在线游戏技术难点分析相关推荐

  1. 游戏外包开发技术难点分析

    游戏开发涉及多个领域的技术,因此在开发过程中可能会遇到很多技术难点.今天和大家分享一些常见的游戏开发技术难点,希望对大家开发游戏有一定帮助.北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合 ...

  2. BigWorld Pty. Ltd.是一家全球领先的大型多人在线游戏(MMOG)开发解决方案供应商...

    BigWorld Pty. Ltd.是一家全球领先的大型多人在线游戏(MMOG)开发解决方案供应商.其开发套件为网络游戏提供了一整套解决方案,可大幅度提高游戏产品质量,并使用户大大降低游戏开发成本,从 ...

  3. 大型多人在线游戏服务器架构设计

    由于大型多人在线游戏服务器理论上需要支持无限多的玩家,所以对服务器端是一个非常大的考验.服务器必须是安全的,可维护性高的,可伸缩性高的,可负载均衡的,支持高并发请求的.面对这些需求,我们在设计服务器的 ...

  4. 快节奏多人在线游戏网络入门系列教程(1):简介

    简介 该系列教程主要讨论快节奏多人在线游戏的网络相关的技术和算法.这是该系列教程的第一章,如果你对多人在线游戏有一定了解,可以跳过本章. 开发任何一款游戏都是一个挑战性的任务.而多人在线游戏增加了更多 ...

  5. 实时多人在线游戏研究(同步和延迟)

    本文讨论实时多人在线游戏的服务器和客户端技术. 实时多人在线游戏主要包括2类,FPS(quake系列,UT系列,CF等),ACT(DNF,龙之谷这类) 其共同特点是需要用户操作尽快的得到体现.并且所有 ...

  6. 使用FLEX3开发大型多人在线游戏

    使用FLEX3开发大型多人在线游戏 2009-09-02 10:07 使用FLEX3开发大型多人在线游戏 收藏 使用FLEX3开发大型多人在线游戏 大型多人在线游戏(MMO)技术已经涉足到各种软件形式 ...

  7. 大型多人在线游戏开发

    http://book.csdn.net/bookfiles/329/index.html 书名:大型多人在线游戏开发 作者:(美)亚历山大 编,史晓明 译 来源:人民邮电出版社 出版时间:2006年 ...

  8. 使用Adobe Flex 3开发大型多人在线游戏

    使用Adobe Flex 3开发大型多人在线游戏 2011年03月28日 大型多人在线游戏(MMO) 技术已经涉足到各种软件形式中了. 当我们还在思考MMO 时, 多人游戏已经使很多玩家能够实时连接进 ...

  9. 秒杀场景的业务和技术难点分析

    秒杀场景的业务和技术难点分析开始: 一.秒杀业务需求分析 1.秒杀发生的时间节点 一般都是在新品上市,促销的时候:价格低廉 2.秒杀的目的 通过发布一定数量的秒杀商品达到促进销售的目的,吸引用户关注: ...

最新文章

  1. 现在的教育:感慨之一
  2. Oracle: SQL组合不同字段作为一个查询条件
  3. ScheduleJobFactory
  4. PHP后台管理-基于Thinkphp5.0开发
  5. [C#] TestHttpPost:测试Http的POST方法的小工具
  6. (休息几天)读曼昆之微观经济学——供给需求和政府政策
  7. suse12 sp4,sp5镜像资源分享
  8. 牛牛的汉诺塔(记忆化搜索)
  9. 23.Consent 代码重构
  10. android更改app背景颜色,使用AppCompat更改操作栏的背景颜色
  11. 相机标定与3D重建(0)标定板说明
  12. 【linux虚拟机使用yum安装MySQL+修改密码】
  13. 996 马云再谈996:理性讨论比结论更重要!
  14. 概念结构设计、逻辑结构设计、物理设计的区分
  15. autojs-获取api接口JSON值
  16. XAMPP的ssl证书安装
  17. 太吾绘卷加载不进去_《太吾绘卷》高难度快速读书以及获得门派支持度方法
  18. 我的10年计划[经济学基础]
  19. 频谱分析仪的工作原理
  20. Spring Cloud Alibaba —— 分布式事务组件

热门文章

  1. 斐波那契数列通项公式的推导证明----举一反三
  2. 重置数据this.$options.data()
  3. 【第 07 章 基于主成分分析的人脸二维码识别MATLAB深度学习实战案例】
  4. 单片机测距雷达c语言代码,51单片机超声波测距倒车雷达Proteus仿真+源代码
  5. BL200EC如何与欧姆龙相连
  6. 【visum工作笔记】之十一 —— Import EMME/2 QA
  7. Spring-Boot
  8. 深入理解Java自动装箱拆箱机制(Autoboxing and unboxing)
  9. Day25常见的内置模块
  10. 教你在windows系统 VMware 软件中安装Ubuntu(附图文教程)超详细