我有一款1v1坦克游戏,运行在一个HTML文件中。这周我添加了在线多人模式。点击按钮,会匹配与其他人点击同一按钮的人。匹配系统使用Cloudflare Durable Objects。一个全球队列将两个玩家配对,并将两个客户端相同的世界种子传递给他们,两个客户端会构建相同的地图,然后浏览器通过中继交换状态25次/秒。这个部分在第一次部署时就工作了。两个陌生人,真实网络,坦克在同步。 我预计这个部分会很难。实际上并不是。窗口是竞技场。 canvas.width = window.innerWidth。我的台式机是一个1920x1080的场地,我的手机是390x844,两台机器之间发送坐标。位置(400,300)是两个不同的地方。每个网络编码指南都提醒这个问题。 我没有读过任何一本。一个isMobile分支破坏了一个共享种子。两个客户端获得相同的数字,但构建了不同的地图。障碍生成器的一行代码: const target = isMobile ? 7+rnd(4) : 8+rnd(6)。它改变了障碍数量,所以两个客户端绘制了不同的随机障碍数,之后的所有内容都偏移了。种子是正确的,但无用。一个早期的返回杀死了三个东西。客人在updateObstacles()函数中提前返回了一行,位于gameFrame++之前,这是共享的动画时钟,而不是障碍逻辑。客人障碍永远不会旋转。帧驱动的颜色冻结。场地只有在接收到快照时才会移动,这在手机上读起来像是跳过。三个症状,一个行。 我修复了一个瞬移写了一个不同的瞬移。 坦克在竞技场边缘包裹。一个普通的lerp将远程坦克拖回地图的另一侧,当它包裹时,所以我写了一个感知包裹的插值 %W。 坦克生活在 -10..W+10,而不是 0..W。我的模数将合法位置拉回了内部。游戏已经有一个 wrappedDelta 帮助函数。对手AI已经理解了包裹的概念几个月了。我的网络编码没有使用它。一个尾随的 // 删除了一行。我的编辑目标是前缀的更长行,我的替换结束于注释。剩下的那行代码 MP.searchT0 = Date.now();落在注释之后消失了。 搜索倒计时计算了 15 - (Date.now()-0)/1000,约为负1.78亿,立即过期,并且每次匹配都会将两个玩家都dump到CPU上。 语法检查通过。测试套件通过。功能已经死了。我的测试通过了所有内容。两个客户端都是同一台机器上的同一台机器。一个测试套件无法发现只有在两个设备之间才存在的错误。两台机器无法独立做出任何决定。这就是整个教训。每个错误都是一个客户端假设另一个客户端不共享的值:它的大小,时钟,RNG流,胜利者。 告诉法是代码将一个值当作局部数字处理,而实际上它是共享状态。 我指示一个AI编写代码。设计和测试是我的。它写了上述每个错误,完全自信,我通过查看两屏幕找到了所有它们。免费,零广告,零账户,试试,然后在反馈中骂我,直到我正确: https://www.hammeranvilbrew.com/vibetanks 或在Itch https://mdawg74.itch.io/vibe-tanks