使用浮点运算的多人实时战略游戏中如何保持同步

7
我正在使用C#编写2D空间RTS游戏。单人模式可以正常运行。现在我想添加一些多人功能。我进行了谷歌搜索,似乎只有一种方法可以在没有强大网络连接的情况下让成千上万的单位不断移动:通过网络发送命令并在每个玩家处运行相同的模拟。

现在有一个问题,整个引擎都在使用双精度数。浮点计算非常依赖于编译器优化和CPU架构,因此很难保持同步。它根本不是基于网格的,并且具有简单的物理引擎来移动太空船(太空船具有脉冲和角动量……)。因此,重新编码整个内容以使用定点数将相当麻烦(但可能是唯一的解决方案)。

到目前为止,我有2个选择:

  • 告别当前代码,并从头开始使用整数
  • 仅在具有足够带宽以使8个玩家拥有数千个单位并在(几乎)每个帧中发送位置和方向等时,才使游戏仅限于局域网...
因此,我正在寻找更好的意见(甚至是关于迁移代码到定点数而不会搞砸一切的提示...)

每个玩家的电脑是否需要模拟所有内容,还是只需模拟他们所看到的内容? - Jason Aller
他们必须模拟一切。 - Calmarius
4个回答

2
您的所有客户端都将使用相同的二进制文件,因此编译器优化对同步问题没有影响。
另外,如果您只计划针对一个架构进行定位(或者至少仅允许在同一架构上玩对战),那么这也无关紧要。
我已经使用C#中的浮点数来开发iPhone和桌面游戏,它们都给出了相同的结果,尽管iPhone是ARM架构,桌面是x86架构。
只需确保游戏进行完全相同的计算,您就可以放心了。
如果一切失败,只需将游戏中所有的float实例替换为标准的固定点算术类。这样,您可以100%确定在架构之间进行的计算是确定性的,尽管固定点算术的本质可能会对您的游戏产生不利影响。

4
即使有两个完全相同的架构,由于浮点数处理器控制字、CPU指令根据管道使用重新排序等原因,它们的浮点数结果可能会有所不同。 - Kylotan
我知道CPU会重新排序指令,但我不相信它会以一种打破确定性的方式重新排序浮点运算的操作。 - Peter Alexander
1
据某些地方所述,浮点运算不是关联的(由于舍入),一位差异可能很重要,可以引起不同步。 甚至不同版本的.NET也可能有不同的JIT优化器,这可能导致不同的结果。而且,我不能要求我的用户拥有.NET 2.0(因为我也希望它能在Linux上的Mono上工作)。 因此,我决定创建一个固定点类,并根据以下内容将(几乎)所有双替换为: https://dev59.com/03RB5IYBdhLWcg3wgHWr (他可能面对了完全相同的问题) - Calmarius

1
我有点晚了才回应,但从游戏安全性的角度来看,模拟应该只在服务器/主机上运行(即:不要相信客户端,他们可能会作弊):
  1. 客户端只应向服务器发送其移动/命令(服务器会丢弃错误输入或将其限制在游戏限制范围内,因此客户端说“我以每秒10,000米的速度奔跑”会被服务器限制为10m/s)。

  2. 服务器/主机只告诉客户端发生在其视野内的事情(即:当一个玩家坐标为0,0时,如果他/她只能看到半径50个单位的范围内,他/她不会得知200,0处两个AI正在战斗)。

第二部分节省了带宽 - 服务器/主机上的模拟可能有成千上万个对象需要管理,但客户端只需要知道自己视野范围内的100或200个事物。

唯一的问题是动态火力(子弹、导弹等)的射程可能大于客户端的视野半径。服务器/主机告诉客户端它们的起点和初始轨迹/目标对象,然后客户端根据相同的规则模拟其路径,但击杀只在服务器/主机的模拟中有效。

在传输之前序列化客户端特定的世界状态并进行压缩也可以带来巨大的优势,特别是如果您的类属性仅在需要时公开。(我通常避免使用XML,但我们通过将其序列化为XML并压缩它而不是将其序列化为二进制格式并压缩它,在一个应用程序中显着提高了压缩比率。我怀疑所使用的ASCII字符的有限范围对此有影响,但结果可能因人而异。)


实际上,在每个客户端模拟世界的P2P游戏中确保安全性非常容易。你所要做的就是相互发送模拟数据的回合校验和(如资源计数、建筑计数等),如果有人发送了不同的校验和,那么他要么是失步或作弊,你只需将其踢出游戏即可。 - Smilediver

0

一种常见的技术是让所有客户端定期向其他客户端描述其当前状态。

当两台计算机对一个对象的状态存在分歧时,可能是由于浮点误差引起的,游戏会有一些规则来确定哪个是正确的,并且所有客户端都会进行调整以匹配它。


正如他在原帖中所述,游戏状态将由数千个对象组成,使得整个状态同步变得不可行。我不知道有哪个实时策略游戏使用这种方法——它们都通过同步输入并确保模拟是确定性的来进行同步。 - Peter Alexander

0
你具体是用double做什么的?能不能用decimal代替?
通常服务器会存储所有玩家单位的状态(位置/方向/类型)。
当玩家1移动一个单位时, 要么...发送移动指令到服务器 要么...发送更新后的状态到服务器
当玩家客户端需要渲染场景时,服务器会返回所请求范围内所有单位位置的状态信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接