« 主题论坛的一些想法 | 返回首页 | 开发笔记 (13) : AOI 服务的设计与实现 »

开发笔记(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

位置同步可以通过加快速度来处理,但是战斗动作的同步怎么处理比较好,加快动作的播放,还是直接取最近的动作,抛弃过时的动作?
一直有一种想法,有可能是behavior这个模式本身的问题,导致做一些同步或者所谓打击感的时候不会让玩家产生那么直观的感觉
怪物公司同学回头又开始看 paper 重新研究 可以推荐一下哪几篇paper么?
在一些回合制的网络游戏中,其实也存在一些及时的动作。比如百战天虫里面虫子的移动等,像这样的动作怎样解决比较适合呢?
您好,关于网络延迟引起的瞬移现象和采用的同步算法有关系吗?如果采用DR算法还会出现瞬移现象吗?目前网游中大多数采用什么同步算法?
其实有没有服务器段的时间戳无所谓啊,只要有自己的和对方的时间戳就可以推算整个网络的延迟,至少我们现在是这么弄的。如果我错了,欢迎批砖
当前位置并不是绝对坐标,是相对目的位置的偏移,因此,用的5.3的定点小数,2个字节完成偏移数据. 把小于-14,大于14的偏移当作位置不同步,需要立即重置位置处理.
并不对时间进行校对工作(尽管有这样的代码,但仅用于长CD的时间判断),认为客户端到服务器端的数据包延迟是固定的.为了弥补这些问题,在其他网络包,如站住/转向/施法消息里,都需要附带当前位置.缺点是数据冗余多,数据量大.但都可以通过控制精度等手段压缩数据. 缺点是服务器CPU压力大,因为要进行客户端一样的移动逻辑.
我目前这么处理的: 在服务器端进行客户端完全一样的位置移动逻辑,移动协议里包含当前位置/目的位置/速度信息等信息.当前位置精度较低,速度只有4种模式可选:慢走/走/跑/急跑.通过适当的组织数据,利用SSE指令转定点小数.因此,冗余数据较多,但数据量控制在10个字节以内. 这样以后,当前位置可以很好的验证和同步位置. 在客户端,玩家自己按照固定逻辑行走,服务器通过玩家发送的当前位置来验证是否位置偏差过大,以及防加速.服务器端按照自己的逻辑移动到新位置. 在客户端,别的玩家的行走速度不是固定的,会根据网络包的当前位置/在本机的位置/目的位置 对速度进行动态的微调.
其实博主让我学习到的精神挺对的!
说的不错。 学习了
引用: Aply 同学已经在内网搭建了模拟环境,模拟各种糟糕的网络环境。测试恶劣环境下的表现,我们有比当年更逼真的工具来实现。 提问:对这个话题比较感兴趣,是纯软件方式模拟网络环境还是使用硬件方式?
大哥。类似于道具在游戏中使用,加装备,比如追加装备,成功率考虑的因素在程序中应该通过什么参数去控制。无双这类型。
我们的游戏也采用和这类似的算法
关于“一秒的周期”,应该考虑用一个动态的间隔时间。不过如果是WOW这类型的话,对位置的容忍度还是很宽泛的吧,可能不用太纠结,做好客户端防作弊就好了。心理上匿了。
感觉搞的太复杂了,其实我认为只要考虑网络条件较好的情况就可以了(比如延迟在200ms之内),因为要是网络条件太差,怎么做体验都不会好。我指的是即时游戏。棋牌或者回合制不在上述情况中。
有个问题就是,如果是从高空掉落,或者是掉下来什么的,考虑到重力因素,这个时候这样同步会出问题吧?
关注学习 云大我好崇拜你
喜欢游戏编程,只是现在程度一点都没有,什么都听不懂。没关系,慢慢积累
说得真不错!
关注学习中!
关注学习中!

Post a comment

非这个主题相关的留言请到:留言本