草庐IT

javascript - 多人游戏 Action 同步

coder 2025-02-13 原文

我正在开发一款多人游戏,但我在同步玩家时遇到了问题。

当玩家按下其中一个移动键(W、A、S、D)时,客户端会发送一个关于按下按钮的数据包,服务器会根据按下的键设置速度,并向所有附近的玩家发回新速度。

当玩家释放按键时,客户端发送一个数据包,服务器将玩家速度设置为 0,0,并将位置和速度发送给附近的所有玩家。

所以问题是当我释放按键时,大部分时间播放器会跳回。

我该如何解决这个问题?

我正在使用 socket.io。

客户端:

   socket.on('positionEntity', function (data) {
        console.log((data.x - entities[data.id].x)+" "+(data.y - entities[data.id].y));
        entities[data.id].setPosition(data);
    });

    $(document).keyup(function(e) {
        if (e.keyCode == 87) {
            keys.W = false;
            socket.emit("stopMove", {dir: 0, time: Date.now()});
        }

        if (e.keyCode == 65) {
            keys.A = false;
            socket.emit("stopMove", {dir: 1, time: Date.now()});
        }

        if (e.keyCode == 83) {
            keys.S = false;
            socket.emit("stopMove", {dir: 2, time: Date.now()});
        }

        if (e.keyCode == 68) {
            keys.D = false;
            socket.emit("stopMove", {dir: 3, time: Date.now()});
        }
    });

    $(document).keydown(function(e) {
        if (e.keyCode == 87 && !keys.W) {
            keys.W = true;
            socket.emit("startMove", {dir: 0, time: Date.now()});
        }

        if (e.keyCode == 65 && !keys.A) {
            keys.A = true;
            socket.emit("startMove", {dir: 1, time: Date.now()});
        }

        if (e.keyCode == 83 && !keys.S) {
            keys.S = true;
            socket.emit("startMove", {dir: 2, time: Date.now()});
        }

        if (e.keyCode == 68 && !keys.D) {
            keys.D = true;
            socket.emit("startMove", {dir: 3, time: Date.now()});
        }
    });

服务器端:

socket.on('startMove', function(data) {
    if (data.dir == 0) socket.player.setMotionY(-5);
    if (data.dir == 1) socket.player.setMotionX(-5);    
    if (data.dir == 2) socket.player.setMotionY(5);
    if (data.dir == 3) socket.player.setMotionX(5);

    io.sockets.emit("positionEntity", socket.player.serializePosition());
});

socket.on('stopMove', function(dir) {
    socket.player.setMotionX(0);
    socket.player.setMotionY(0);

    io.sockets.emit("positionEntity", socket.player.serializePosition());
});

最佳答案

这是您正在处理的一项非常复杂的任务,也是我作为宠物项目的一部分所做的事情 ;)

您正在开发客户端-服务器架构游戏,因此服务器是游戏逻辑和决策的最终权威。由于延迟,您当前处理渲染的方式会使速度和方向突然发生明显变化(正如您所注意到的!)

诀窍是缓冲远程玩家的移动信息,因此您总是会稍微延迟地渲染玩家。我在我的项目中保持原始状态,只使用位置数据,不使用加速度或速度。例如,当玩家 A 在他的机器上移动时,不会立即发送命令以接收确认,他移动并在我的网络发送循环的下一个滴答声中(每秒 10 个滴答声)他的位置被触发到更新所有客户端的服务器这个新位置附近。这些客户端为每个“远程”播放器都有一个缓冲区,在呈现更新之前将每个位置存储一段时间(100 毫秒)。通过这种方式,客户端的渲染会有轻微的延迟,但我可以在每个位置坐标之间进行插值(平滑 Sprite /模型的过渡),以实现具有速度和加速度错觉的平滑运动。

客户端代码的基本插值函数。该系统最多只为每个远程玩家排队两个更新,其中更新数组中的索引 0 是两个中较早的一个。所以索引 0 可能是远程玩家位置 0ms,索引 1 可能是远程玩家在 100ms 的位置。

interpolate: function() {
  var timeDifference = new Date().getTime() - this.serverUpdates[1].time;
  // Percentage of time passed since update was received (I use 100ms gaps)
  var interPercent = (timeDifference) / 100;

  // Latest updates (index 1 is the newest state)
  var s1 = this.serverUpdates[0],
    s2 = this.serverUpdates[1];

  // Need to lerp between values provided in latest update and older one
  var p = (new Vector3(s2.position)).subtract(new Vector3(s1.position));
  p = p.timesScalar(interPercent);

  // New position is the older lerped toward newer position where lerp 
  //percentage is the time passed 
  this.position = new Vector3(s1.position).add(p);

  // Now update rotation in a smooth manner
  var rotationDifference = (s2.rotation - s1.rotation);
  if (rotationDifference && rotationDifference != 0) {
    this.rotation = s1.rotation + (rotationDifference * interPercent);
  }
},

在该代码中,我收到的更新间隔大约为 100 毫秒,因此在时间 0 位置是 s1,时间 100 毫秒是位置 s2。因此,如果自从我们收到 s2 以来已经过去了 50 毫秒,那么实体在两个位置之间为 50%。这很好地满足了我的需要,但可能不适用于其他类型的游戏或可能需要调整。

这些资源是解释网络游戏和处理延迟的良好开端,您会惊讶于实现插值和外插对远程客户端游戏流畅度的影响。

http://gafferongames.com/networking-for-game-programmers/what-every-programmer-needs-to-know-about-game-networking/

https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization <->

https://developer.valvesoftware.com/wiki/Lag_compensation

祝你好运:)

关于javascript - 多人游戏 Action 同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18114901/

有关javascript - 多人游戏 Action 同步的更多相关文章

  1. ruby-on-rails - rails : How to make a form post to another controller action - 2

    我知道您通常应该在Rails中使用新建/创建和编辑/更新之间的链接,但我有一个情况需要其他东西。无论如何我可以实现同样的连接吗?我有一个模型表单,我希望它发布数据(类似于新View如何发布到创建操作)。这是我的表格prohibitedthisjobfrombeingsaved: 最佳答案 使用:url选项。=form_for@job,:url=>company_path,:html=>{:method=>:post/:put} 关于ruby-on-rails-rails:Howtomak

  2. ruby-on-rails - 如何在 Rails Controller Action 上触发 Facebook 像素 - 2

    我有一个ruby​​onrails应用程序。我按照facebook的说明添加了一个像素。但是,要跟踪转化,Facebook要求您将页面置于达到预期结果时出现的转化中。即,如果我想显示客户已注册,我会将您注册后转到的页面作为成功对象进行跟踪。我的问题是,当客户注册时,在我的应用程序中没有登陆页面。该应用程序将用户带回主页。它在主页上显示了一条消息,所以我想看看是否有一种方法可以跟踪来自Controller操作而不是实际页面的转化。我需要计数的Action没有页面,它们是ControllerAction。是否有任何人都知道的关于如何执行此操作的gem、文档或最佳实践?这是进入布局文件的像素

  3. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  4. ruby-on-rails - 优雅的 Rails : multiple routes, 相同的 Controller Action - 2

    让多条路线去同一条路的最优雅的方式是什么ControllerAction?我有:get'dashboard',to:'dashboard#index'get'dashboard/pending',to:'dashboard#index'get'dashboard/live',to:'dashboard#index'get'dashboard/sold',to:'dashboard#index'这很丑陋。有什么“更优雅”的建议吗?一个类轮的奖励积分。 最佳答案 为什么不只有一个路由和一个Controller操作,并根据传递给它的参数来

  5. ruby - 我需要从 facebook 游戏中抓取数据——使用 ruby - 2

    修改(澄清问题)我已经花了几天时间试图弄清楚如何从Facebook游戏中抓取特定信息;但是,我遇到了一堵又一堵砖墙。据我所知,主要问题如下。我可以使用Chrome的检查元素工具手动查找我需要的html-它似乎位于iframe中。但是,当我尝试抓取该iframe时,它​​是空的(属性除外):如果我使用浏览器的“查看页面源代码”工具,这与我看到的输出相同。我不明白为什么我看不到iframe中的数据。答案不是它是由AJAX之后添加的。(我知道这既是因为“查看页面源代码”可以读取Ajax添加的数据,也是因为我有b/c我一直等到我可以看到数据页面之后才抓取它,但它仍然不存在)。发生这种情况是因为

  6. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  7. ruby-on-rails - Rails Rspec 测试 Controller 新 Action - 2

    我正在尝试在我的Controller中测试新操作。目前它看起来像这样:Controllerdefnew@business=Business.new@business.addresses.buildend规范describe'GET#new'doit'assignsanewbusinessto@business'doget:newexpect(assigns(:business)).tobe_a_new(Business)endend我想测试“@business.addresses.build”这一行。我该怎么做?提前致谢! 最佳答案

  8. python - Ruby 或 Python 的 3d 游戏引擎? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion是否有适用于这些的3d游戏引擎?

  9. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

    我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

  10. ruby-on-rails - ApplicationController :Class 的未定义方法 `caches_action' - 2

    我正在尝试升级到Rails4beta1,但遇到了一些问题。简而言之,这就是我的应用程序Controller的样子。classApplicationControllercaches_action在Rails4中移到了它自己的gem中,因此包含gem应该可以解决问题。gem"actionpack-action_caching",github:"rails/actionpack-action_caching"但是当我运行我的请求规范或在浏览器中访问该应用程序时,我收到此错误。app/controllers/application_controller.rb:3:in`':undefinedm

随机推荐