IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

位置同步策略

云风的 BLOG 2012-09-10 23:36:51 累计浏览 1,565 次
本机暂存

    最近两周,陆续有些新同事到岗,或即将到岗。所以我不想过多的再某个需要实现的技术细节上沉浸进去了。至少要等计划的人员齐备,大家都有事情做了以后,个人再好领一块具体事情做。所以属于我个人的代码不多。我主要也就是维护一下前面我自己实现的模块,以及把之前写的一些代码交接给下面具体负责的同学。

    哦,这里值得一提的是,我写的 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 。短于这个时间的都认为是同时发生。

    客户端发送一个本地时间量给服务器,服务收到包后,夹带一个服务器时间返回给客户端。当客户端收到这个包后,可以估算出包在路程上经过的时间。同时把本地新时间夹带进去,再次发送给服务器。服务器也可以进一步的了解响应时间。到此为止。

    客户端时间和服务器时间具体是什么含义不重要,数值也不必统一。我们简单认为,这个时间值是各自的本地时间就好了。两边分别利用数值计算时差。

    由于我们暂时只解决位置同步问题。

    首先信任客户端的数据。客户端发送自己的位置坐标和运动矢量(包含有方向和速度)以及当前时间给服务器。

    服务器收到后,认为在某一时刻(客户端时间),这个玩家在什么位置,怎样运动的。根据对时求得的时差和估算的延迟,可以预计客户端当前时刻(服务器时间)应该是什么状态(位置以及运动矢量)。把这个信息广播给所有的玩家。

    每个玩家收到后,再根据他们之前估算出来的时差以及延迟,得到本地时间当时,所有玩家的状态。

    因为玩家运动是连续的。上面得到的状态和他们看到的这些角色的时间状态会有偏差。校准偏差分两种情况讨论。

    一种,收到的信息是属于其它玩家的。我们从最新得到的状态信息,预测一段时间之后(比如一秒后的状态),用一条直线运动去修正。即,设想一秒后这个玩家在哪里,然后反推回现在应该用什么速度运动可以在一秒后到达那个地方。

    另一种,收到的信息是属于自己的,即服务器认可的自己的状态(并广播给别人了)。这个偏差是由于服务器的预测补偿造成的。为了保持用户的操作手感,对于不太极端的偏差,我们全部不修正,而是依然发送客户端自己操作的位置状态给服务器。服务器那边玩家是处于一种离散的运动状态的。而其他人见到你会再做预测补偿;如果和服务器相差过于剧烈,则直接跳转到服务器认可的新位置。

    这里几乎全部相信客户端的行为,以获取最好的操作手感。防止客户端作弊是另外一个话题,也不是不能解决的,但目前不要碰了。

    客户端到底以怎样的频率发送那些位置信息给服务器呢?

    策略应该是这样的:

    每次发送完一个完整的位置信息后,预测服务器看待这个位置信息包一秒后的位置大约在哪里。每次变化做一个累积,一秒内都但不用立刻发送。但每次小的状态改变都和假设的预测位置做一些比较,如果位置偏差比较大,就可以提前发送。否则一直累计到一秒再发送。

    这个一秒的周期可以根据实际测试情况来调整。可能一秒太短,也可能过长了。

    每次收到服务器发送过来的新的玩家位置信息时,都在里面会找到一个时间戳,表识的包发出的服务器时间。客户端可以验算之前的网络延迟是否正确。如果网络延迟稳定在一个固定值,说明没有问题。但如果延迟值为负数,则说明之前的对时流程中网络不稳定(可能是因为上下行时间偏差比较大造成的,也可能是当时服务器负载很大,造成了较大的内部延迟),造成本地时间和服务器时间的时差计算错误。这个时候重新发起对时流程就好了。

同分类推荐文章

  1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
  2. Go 实验特性详解 (2026-06-21 10:05:27)
  3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

查看更多 后端 文章 →

建议继续学习

  1. libcurl中使用curl_easy_getinfo 产生段错误分析 (累计阅读 5,783)
  2. MYSQL数据库网卡软中断不平衡问题及解决方案 (累计阅读 4,766)
  3. 利用vim(gvim)的正则表达式实现代码自动匹配完成(等号两边数据交换) (累计阅读 4,669)
  4. Redis源代码分析 (累计阅读 4,424)
  5. IT人员的必经之路(图解) (累计阅读 4,335)
  6. SysLog个人经验总结和分享 (累计阅读 4,180)
  7. Android最方便的推送框架 (累计阅读 4,153)
  8. 和netstat说再见 (累计阅读 4,132)
  9. Web 开发程序员谈网游服务器开发 (累计阅读 3,997)
  10. ZeroMQ的学习和研究 (累计阅读 3,913)