实时pvp的关键在于对抗延迟和网络同步。
这里先分享几篇非常经典的文章
http://www.skywind.me/blog/archives/131
https://www.zhihu.com/question/36258781
http://www.skywind.me/blog/archives/1048
之前我也分享过一篇关于帧同步的思考,这篇文章最后也分享了几篇非常不错的英文文章。
http://blog.csdn.net/langresser_king/article/details/46756393
简单来理解网络同步就是帧同步和状态同步两种方案。
所谓帧同步就是保证客户端在相同的输入的情况下可以执行出相同的输出结果,这样客户端之间只需要同步玩家操作数据,不需要同步单位的状态数据,客户端自己根据输入进行模拟。这个方案的好处是同步量非常小,服务器不需要处理复杂的同步逻辑,甚至服务器可以没有逻辑只负责同步关键帧数据。坏处是由于服务器不处理逻辑,所以防外挂比较难处理,断线重连也比较麻烦,最大的问题是客户端实现起来比较困难(逻辑写起来容易,但是要保证同步以及复查bug会非常困难,对代码质量要求比较高),比如物理、寻路要自己实现,也有浮点数等需要关注的问题。
帧同步常用于单位非常多的RTS游戏或者对实时性要求非常高的动作或竞技游戏。
状态同步常见于MMO,这种玩家不定的游戏只能用状态同步。服务器处理逻辑,客户端只负责表现,服务器的逻辑驱动客户端运行,客户端之间不一定完全一致,但是最终状态都是一致的。它的好处是服务器处于权威地位,无论是防外挂还是断线重连都比较好处理。为了保证客户端的流畅性,状态同步衍生了一些解决方案,比如航位预测、影子跟随等。
皇室战争的实时pvp同步有这个几个表现:
1、网络卡的玩家只是自己会卡,不会影响到对手
2、断线重连做的非常好,无论什么时候强退游戏,再登陆都可以立即恢复到正确状态
3、网络卡的时候会出现画面暂停,当重新恢复正常的时候可能会快进,也可能会直接同步所有单位到正确状态
4、倒计时并不是严格1秒1秒执行的,可能会跳的非常快,也可能会出现回退
5、玩家出卡并不是立即出卡,而是等待服务器回应,如果服务器没回应则取消出卡
6、双方玩家在某个固定的倒计时游戏状态都是一样的,但是网络卡的玩家从表现上会落后于网络流畅的玩家(这对战斗而言是不利的)
7、没听说有什么外挂
基于上面的这些表现,所以我推测皇室战争的pvp同步是这么实现的(或者说如果让我来做,我会这么处理):
1、基于乐观帧锁定算法实现客户端以及同步框架。关于乐观帧锁定算法可以参考分享的第一篇关于帧锁定算法的文章。
简单来说就是客户端按照逻辑帧(每隔50或者100毫秒一个逻辑帧)执行客户端逻辑,客户端就像一个录像机一样一帧一帧的播放。
普通的帧同步,服务器(或者主机)需要接收到所有客户端的操作数据,然后才允许客户端向下执行。这样一个玩家卡住,所有玩家都要等待。
而乐观帧锁定是服务器自己安装固定的时间间隔同步操作数据,客户端依然需要接收到某个逻辑帧的数据才能执行到这个逻辑帧,否则就需要卡住。不过网络卡的玩家只会自己感觉到卡,不会影响到网络流畅的玩家。
基于coc的技术积累,皇室战争在保证客户端确定性模拟这方面应该没有什么太大的问题。
2、基于上面说的同步方案,服务器每隔固定时间(比如100毫秒)向客户端同步时间戳数据,客户端只允许运行到服务器同步到的时间戳,如果没有最新的时间戳数据,则客户端卡住,等待网络恢复。
客户端的出卡操作是给服务器一个通知,具体什么时刻出卡取服务器时间。这样无论双方玩家怎么卡都能够保证逻辑是正确的。皇室战争毕竟不是实时动作手游,这种出卡(游戏中唯一的操作)延迟对玩家而言一般都是可以接受的。甚至皇室战争本身都对卡牌预置了一个从出卡到部署的延迟,我们可以看到卡牌从出卡到出倒计时开始部署是有一个小的延迟的,这个延迟可能仅仅是网络延迟,也可能是逻辑帧间隔的时间,也可能是一个兼容网络延迟的固定值。
3、皇室战争的战斗服务器应该是有跑战斗逻辑的,这个对服务器而言是有压力的,但是战斗服务器与玩家状态无关,只关心战斗流程,所以可以轻易的部署N台服务器进行平行扩展。 由于服务器有跑一模一样的战斗逻辑,所以当玩家断线甚至退出游戏,再登陆后,可以立即恢复到正确状态,这里就是一个全状态同步。 同样当玩家网络实在糟糕,其运行状态跟服务器差别很大的时候,同样会进行一次全状态同步。
针对这点来说,反正服务端都要跑一份逻辑(无论是用来校验的还是用来搞定断线重连的),那么我更倾向于让服务器进行战斗逻辑运算,然后每帧不仅仅同步时间戳,而且把单位状态都一并同步了(或者仅同步一些关键状态,如A单位开始攻击B单位,B单位死了,C单位向哪里移动,在哪里被攻击了),这样做流量消耗会大些,但是可以降低客户端的开发难度,客户端可以不做那么强的同步保证,一切都依赖服务器的数据来播放就可以保证正确。 同时,当配置或者角色逻辑更新了,可以很大程度上避免客户端的更新(无论是全包更新,还是增量更新),因为所有逻辑都依赖于服务器的同步数据。如果逻辑都在客户端执行的话,当某个单位的逻辑发生变化(c#代码层的修改),那么就必须进行全包更新。这对竞技游戏来说是不利的,因为竞技游戏需要经常调整单位的平衡性或者修正某些会影响到游戏性的Bug。
4、皇室战争在实现上有个细节是非常难以处理的,就是角色之间的碰撞和推攘。单位的移动可以是简单的A*寻路,可以对地图格子设置不同的cost,从而让单位优先从道路走(飞行单位则无此限制)。角色之间并没有避障行为,但是角色之间有推攘。这里可以引入box2d或其他的物理引擎。角色设置好质量,然后角色之间通过物理效果进行推挤。