开发笔记(12) : 位置同步策略
最近两周,陆续有些新同事到岗,或即将到岗。所以我不想过多的再某个需要实现的技术细节上沉浸进去了。至少要等计划的人员齐备,大家都有事情做了以后,个人再好领一块具体事情做。所以属于我个人的代码不多。我主要也就是维护一下前面我自己实现的模块,以及把之前写的一些代码交接给下面具体负责的同学。
哦,这里值得一提的是,我写的 protobuf C 库 慢慢算是可用了。自从提交到 github 上后,有两处 bug 是不认识的网友指出的。当然我自己在用的过程中发现修正的 bug 更多。现在算是基本完善了吧。接下来还会大量使用到,等整个项目做完,应该就基本没问题了。目前主要是用它的 lua binding 。为了用起来更方便,昨天我甚至自己实现了一套 proto 文件的 parser ,作为一个选项,可以不依赖 google 官方提供的工具来编译了。
前两天我们开程序例会。dingdang 主持会议。提到,在工作全面展开前(还有几个程序和策划没有到位),我们最后的一点时间应该把一些技术点解决掉。其中之一就是解决好即时战斗游戏中的位置同步问题。要做到好的操作手感不太容易,至少现在看到的国内的 MMO 没有做的特别让人满意的。我们比较熟悉的网易的产品,天下二,这方面就比较糟糕。
至少要达到 wow 里的水准吧,在网络不稳定,延迟在 200 到 2000 ms 波动时,玩家还要玩的比较舒服。
话说到这里,我想起 6 年前,我们就做过这方面的探索 ,并写了许多代码验证。花了不少的时间。这次怪物公司同学回头又开始看 paper 重新研究。当年和我一起做这块的人不在了,换了一拨人,感觉场景仿佛相识。不过这次技术储备更完备一些,许多工具,Engine 什么的也完整。应该会更顺利吧。
这块 dingdang 认为很重要,如果一开始不做好,以后没完没了的任务堆过来,就不再能回头弄了。天下就是如此。我当年也是这样想的,只不过花了太多时间去弄了,项目开发的节奏控制的不好。下面把我当初理的思路在整理一次,重新列出来,算是个记录。
我们需要简化问题,并先解决一个比较小的问题集。把系统搭建起来,这样可以迭代测试。自己玩过一些,才好改进。所谓,快速原型,吃自己的狗粮。那么现阶段集中做不同玩家的位置同步。仅解决这一个问题。把 3d 客户端和服务器搭起来,可以真正的跑起来。我们的 IT ,Aply 同学已经在内网搭建了模拟环境,模拟各种糟糕的网络环境。测试恶劣环境下的表现,我们有比当年更逼真的工具来实现。
不要把问题想的过于复杂,也不要使用太难理解和繁杂的手段来解决问题。不过陷入一些技术细节。比如,让服务器和客户端校对时间就是一个你想钻进去水都不浅的坑。(见这里 )我们暂时只粗略的构建原型,使用最简洁的方案来做。
首先,我们设计一个最简单的对时协议。即,我们先约定,我们的网络包里的最小时间精度是 10ms ,即 0.01s 。以这个为单位 1 。短于这个时间的都认为是同时发生。
客户端发送一个本地时间量给服务器,服务收到包后,夹带一个服务器时间返回给客户端。当客户端收到这个包后,可以估算出包在路程上经过的时间。同时把本地新时间夹带进去,再次发送给服务器。服务器也可以进一步的了解响应时间。到此为止。
客户端时间和服务器时间具体是什么含义不重要,数值也不必统一。我们简单认为,这个时间值是各自的本地时间就好了。两边分别利用数值计算时差。
由于我们暂时只解决位置同步问题。
首先信任客户端的数据。客户端发送自己的位置坐标和运动矢量(包含有方向和速度)以及当前时间给服务器。
服务器收到后,认为在某一时刻(客户端时间),这个玩家在什么位置,怎样运动的。根据对时求得的时差和估算的延迟,可以预计客户端当前时刻(服务器时间)应该是什么状态(位置以及运动矢量)。把这个信息广播给所有的玩家。
每个玩家收到后,再根据他们之前估算出来的时差以及延迟,得到本地时间当时,所有玩家的状态。
因为玩家运动是连续的。上面得到的状态和他们看到的这些角色的时间状态会有偏差。校准偏差分两种情况讨论。
一种,收到的信息是属于其它玩家的。我们从最新得到的状态信息,预测一段时间之后(比如一秒后的状态),用一条直线运动去修正。即,设想一秒后这个玩家在哪里,然后反推回现在应该用什么速度运动可以在一秒后到达那个地方。
另一种,收到的信息是属于自己的,即服务器认可的自己的状态(并广播给别人了)。这个偏差是由于服务器的预测补偿造成的。为了保持用户的操作手感,对于不太极端的偏差,我们全部不修正,而是依然发送客户端自己操作的位置状态给服务器。服务器那边玩家是处于一种离散的运动状态的。而其他人见到你会再做预测补偿;如果和服务器相差过于剧烈,则直接跳转到服务器认可的新位置。
这里几乎全部相信客户端的行为,以获取最好的操作手感。防止客户端作弊是另外一个话题,也不是不能解决的,但目前不要碰了。
客户端到底以怎样的频率发送那些位置信息给服务器呢?
策略应该是这样的:
每次发送完一个完整的位置信息后,预测服务器看待这个位置信息包一秒后的位置大约在哪里。每次变化做一个累积,一秒内都但不用立刻发送。但每次小的状态改变都和假设的预测位置做一些比较,如果位置偏差比较大,就可以提前发送。否则一直累计到一秒再发送。
这个一秒的周期可以根据实际测试情况来调整。可能一秒太短,也可能过长了。
每次收到服务器发送过来的新的玩家位置信息时,都在里面会找到一个时间戳,表识的包发出的服务器时间。客户端可以验算之前的网络延迟是否正确。如果网络延迟稳定在一个固定值,说明没有问题。但如果延迟值为负数,则说明之前的对时流程中网络不稳定(可能是因为上下行时间偏差比较大造成的,也可能是当时服务器负载很大,造成了较大的内部延迟),造成本地时间和服务器时间的时差计算错误。这个时候重新发起对时流程就好了。
ps. 关于争论。我想到我一个老同学在 douban 上写的日记中引用 The Idea Hunter 的一句话特别有道理:
when one is about to dismiss the other as naive or sth , ask " what if he is right"?
Comments
Posted by: wishes2018 | (22) September 13, 2014 04:17 PM
Posted by: Anonymous | (21) September 15, 2013 01:00 AM
Posted by: leepengg | (20) April 9, 2013 07:41 AM
Posted by: BackMountainBird | (19) July 16, 2012 04:42 PM
Posted by: fiona | (18) June 5, 2012 03:19 PM
Posted by: sailing | (17) March 29, 2012 07:11 PM
Posted by: tearshark | (16) March 12, 2012 01:04 PM
Posted by: tearshark | (15) March 12, 2012 12:58 PM
Posted by: tearshark | (14) March 12, 2012 12:53 PM
Posted by: 宝马会娱乐城 | (13) March 7, 2012 04:31 PM
Posted by: crazy | (12) March 5, 2012 06:49 PM
Posted by: Alex | (11) March 4, 2012 10:21 PM
Posted by: lukai | (10) March 3, 2012 11:34 PM
Posted by: acai | (9) March 3, 2012 10:37 PM
Posted by: Xw.Y | (8) March 3, 2012 10:09 PM
Posted by: firstfish | (7) March 3, 2012 05:40 PM
Posted by: bravestarr | (6) March 3, 2012 11:47 AM
Posted by: unowoo | (5) March 3, 2012 09:44 AM
Posted by: 大笨兔 | (4) March 3, 2012 09:36 AM
Posted by: 拾趣猫 | (3) March 3, 2012 01:29 AM
Posted by: okzai | (2) March 3, 2012 12:47 AM
Posted by: okzai | (1) March 3, 2012 12:46 AM