【问题标题】:JavaScript: How to calculate offsets for a boundary of a moving sprite when it's speed is relative to time?JavaScript:当速度与时间相关时,如何计算移动精灵边界的偏移量?
【发布时间】:2021-04-11 00:45:44
【问题描述】:

我现在遇到了一个困扰我好几天的数学难题。

我正在构建一个 JavaScript 游戏并尝试创建边界坐标来管理精灵的路径和移动,但似乎滞后/抖动/延迟对相互协调移动的不同实体造成了严重破坏。

我相信我必须计算抖动/滞后/偏移并以某种方式将其应用于坐标范围检测和移动功能,但我还没有正确破解代码并缓解未对齐的精灵。

以下是 CodeSandbox 中问题的复制,以及显示该问题的大部分代码: https://codesandbox.io/s/movetime-boundries-issue-example-2prow?file=/src/App.js

  var obj = { x: 10, speed: 250 };
  var obj2 = { x: 100 };

  var objHighestX = { max: 0 };

  var direction = 0;

  var canvas = document.getElementById("mainScene");
  var ctx = canvas && canvas.getContext("2d");
  ctx.imageSmoothingEnabled = false;

  ctx.font = "15px Courier";
  var render = function () {};

  var update = function (modifier) {
    // console.log("Updating");
    ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
    ctx.fillRect(obj2.x, 60, 15, 15);
    if (obj.x > objHighestX.max) {
      objHighestX.max = obj.x;
    }
    ctx.fillText(String("X" + obj.x), 25, 100);
    ctx.fillText(String("Furthest" + objHighestX.max), 125, 100);
    if (obj.x >= obj2.x - 15) {
      direction = 1;
    } else if (obj.x <= 0) {
      direction = 0;
    }
    if (direction === 0) {
      obj.x += obj.speed * modifier;
      ctx.clearRect(obj.x - 7, 9, 17, 17);
      ctx.fillRect(obj.x, 60, 15, 15);
    }
    if (direction === 1) {
      obj.x -= obj.speed * modifier;
      ctx.clearRect(obj.x, 9, 17, 17);
      ctx.fillRect(obj.x, 60, 15, 15);
    }
  };
  var lastUpdate = Date.now();
  // The main game loop
  var main = function () {
    var now = Date.now();
    var delta = now - lastUpdate;
    lastUpdate = now;
    update(delta / 1000);
    render();
    requestAnimationFrame(main);
  };
  main();

如果有人对我的案子有任何建议或疑问,我非常渴望听到。

也许我必须使用变化率来为边界创建偏移量?

我已经尝试过:

    if (obj.x >= obj2.x - (15 * 1 * modifier)) 

但是我还没有把这个弄下来。提前谢谢大家。

【问题讨论】:

    标签: javascript math canvas html5-canvas game-development


    【解决方案1】:

    首先,您的增量时间计算不完整。

    var now = Date.now();
    var delta = now - lastUpdate;
    lastUpdate = now;
    update(delta / 1000);
    

    如果您现在请求通过 requestAnimationFrame 调用 update(),则作为参数传递的数字将是最后一帧和当前帧之间传递的毫秒数。因此,如果屏幕刷新率为 60hz,则大约为 16.6ms。

    虽然这个值本身没有意义 - 您需要将它与目标值进行比较。

    假设我们想要实现 30fps 的帧速率 - 等于 ~33.3ms。如果我们把这个值从上面的 16.6ms 中除掉,我们得到大约 0.5。这是完全有道理的。我们想要 30fps,显示器以 60hz 刷新,所以一切都应该以一半的速度移动。

    让我们修改您的 main() 函数以反映这一点:

     var main = function() {
       var targetFrameRate = 30;
       var frameTime = 1000 / targetFrameRate;
       var now = Date.now();
       var delta = now - lastUpdate;
       lastUpdate = now;
       update(delta / frameTime);
       render();
       requestAnimationFrame(main);
     };
    

    第二个问题是update() 函数本身。 让我们看看下面的代码块:

    if (direction === 0) {
      obj.x += obj.speed * modifier;
      ctx.clearRect(obj.x - 7, 9, 17, 17);
      ctx.fillRect(obj.x, 60, 15, 15);
    }
    

    这意味着,无论 obj 当前在哪里,将其向右移动一定量。此时我们缺少边界检查。如果我们向右移动,您需要检查它是否离开边界。如果是这样,只需将其移动到边界旁边即可。

    类似这样的:

    var maxX=100;
     if (direction === 0) {
       var speed = obj.speed * modifier;
       if (obj.x + obj.width + speed > maxX) {
         direction = 1;
         obj.x = maxX - obj.width;
       } else {
         obj.x += speed;
       }
     }
    

    【讨论】:

    • 哇!这绝对有道理!非常感谢您抽出宝贵时间阅读并提出您的调查方法。在将这些从头开始的游戏引擎中插入并查看它的实际效果后,我将在今晚更新此内容。再次感谢您的帮助和精彩的阐述。
    【解决方案2】:

    在碰撞帧中保持正确的速度

    我注意到对象总是在移动,这意味着给定的答案不能正确解决问题。

    如果对象具有恒定速度,则它不应在帧之间减速

    插图显示物体移动

    • 在顶部,它会在不中断的情况下移动多远。
    • 在碰撞点的中心。请注意,要保持相同的速度,仍然需要走很远的距离。
    • 在底部,对象向左移动了剩余的距离,这样总的行进距离与速度相匹配。

    为了保持速度,帧之间的总行驶距离必须保持不变。将对象定位在碰撞点会减少行进的距离,因此可以大大降低碰撞帧期间对象的速度

    正确计算如下

     const directions = {
         LEFT: 0,
         RIGHT: 1,
     };
     const rightWallX = 100;
     const leftWallX = 0;
    
     if (obj.direction === directions.RIGHT) {
         obj.x = obj.x + obj.speed;
         const remainDist = (rightWallX - obj.width) - obj.x;
         if (remainDist <= 0) {
             obj.direction = directions.LEFT;
             obj.x = (rightWallX - obj.width) + remainDist;
         }
     } else if (obj.direction === directions.LEFT) {
         obj.x = obj.x - obj.speed;
         const remainDist = leftWallX - obj.x;
         if (remainDist >= 0) {
             obj.direction = directions.RIGHT;
             obj.x = leftWallX + remainDist;
         }
     }
    

    【讨论】:

    • 我真的很喜欢这个答案!通过计算剩余距离并根据该数字和对象的速度进行程序移动,我已经完美地阻止了边界的溢出。盲人,你肯定有一些深刻的见解:) 非常感谢你的精彩回答。
    • @Blindman67 虽然你的观点当然是正确的,但我想知道我是否应该发布另一个答案,声称你的“没有正确解决问题”,因为它忽略了物体失去能量的事实当它从障碍物反弹时,碰撞和潜在的空气阻力会产生摩擦,因此物体应该更靠近障碍物?
    • @obscure 这个问题不包括任何阻力或碰撞损失,但它确实具有恒定的速度,这就是我发布的原因
    • @Blindman67 没问题,忘了我说什么了。今天不是最好的一天,所以也许我把事情太私人化了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-24
    • 1970-01-01
    相关资源
    最近更新 更多