当速度过高时,碰撞检测失败

时间:2017-04-06 00:29:39

标签: javascript collision-detection game-physics vector-graphics physics-engine

当一个(或两个)具有非常高的速度时,我试图检测两个球之间的碰撞时遇到这个问题。我想这是一个非常普遍的问题,我理解为什么会这样。我的猜测是解决方案将与衍生品有关,我已经设计了一些东西,但如果有一个已知的解决方案,我不想“重新发明轮子”。

任何可能使我的道路充满信心的事情都会得到充分肯定!

我做了这个非常简单的例子。如果两个球的速度均为1.5或3,则会发生碰撞。如果我们使用更高的东西,比如 50,它会失败。

<html>
<head>
  <title>Collision test</title>
</head>
<body>

  <canvas id="canvas"></canvas>

  <script>

    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    const width = window.innerWidth;
    const height = window.innerHeight-4;
    const center = { x: width/2, y: height/2 };

    canvas.width = width;
    canvas.height = height;

    // Ball Class Definition
    class Ball {
      constructor(x, y, mass, direction, speed, color) {
          this.x = x;
          this.y = y;
          this.vx = (Math.cos(direction) * speed) || 0;
          this.vy = (Math.sin(direction) * speed) || 0;
          this.mass = mass || 1;
          this.radius = mass * 3;
          this.color = color || "#000000";
      }

      update() {
          this.x += this.vx;
          this.y += this.vy;
      }
    }

    let speedA = 1.5;
    let speedB = 1;

    // Create two balls that will collide
    let ballA = new Ball(center.x - 300, center.y, 3, Math.PI*2, speedA, "green");
    let ballB = new Ball(center.x + 100, center.y, 2.2, Math.PI, speedB, "green");


    // Main update/draw function
    function draw() {
      window.requestAnimationFrame(draw);

      ctx.clearRect(0,0, width, height);

      ballA.update();
      ballB.update();


      handleCollisions(ballA, ballB);

      // Draw Ball A
      ctx.beginPath();
      ctx.arc(ballA.x, ballA.y, ballA.radius, 0, Math.PI * 2, false);
      ctx.fillStyle = ballA.color;
      ctx.fill();

      // Draw Ball B
      ctx.beginPath();
      ctx.arc(ballB.x, ballB.y, ballB.radius, 0, Math.PI * 2, false);
      ctx.fillStyle = ballB.color;
      ctx.fill();
    }

    // Detect and handle collision
    function handleCollisions(p1, p2) {
        let xDist, yDist;
        xDist = p1.x -  p2.x;
        yDist = p1.y -  p2.y;

        let distSquared = xDist*xDist + yDist*yDist;

        //Check the squared distances instead of the the distances, same result, but avoids a square root.
        if(distSquared <= (p1.radius + p2.radius)*(p1.radius + p2.radius)){
            let xVelocity = p2.vx - p1.vx;
            let yVelocity = p2.vy - p1.vy;
            let dotProduct = xDist*xVelocity + yDist*yVelocity;

            //Neat vector maths, used for checking if the objects moves towards one another.
            if(dotProduct > 0){
              let collisionScale = dotProduct / distSquared;
              let xCollision = xDist * collisionScale;
              let yCollision = yDist * collisionScale;

              //The Collision vector is the speed difference projected on the Dist vector,
              //thus it is the component of the speed difference needed for the collision.
              let combinedMass = p1.mass + p2.mass;
              let collisionWeightA = 2 * p2.mass / combinedMass;
              let collisionWeightB = 2 * p1.mass / combinedMass;
              p1.vx += collisionWeightA * xCollision;
              p1.vy += collisionWeightA * yCollision;
              p2.vx -= collisionWeightB * xCollision;
              p2.vy -= collisionWeightB * yCollision;
            }
        }

    }

    draw();

    </script>
</body>
</html>

我将此code添加到JSBin。

2 个答案:

答案 0 :(得分:1)

问题在于碰撞检测是使用球的位置之间的距离来完成的,但是如果它们移动得太快,那么它们可能会跳过&#34;跳跃&#34;彼此正好并且永远不会足够接近以便检测到碰撞。

一种解决方案可能是计算每一个球的点,以及每个球的各自时间。然后,比较每个球的点数列表,看看球是否足够接近碰撞时间。换句话说,在帧之间插入它们的位置并检查这些插值位置处的碰撞。你必须要小心,因为即使球可能通过足够接近碰撞的点,他们也需要在大约同一时间这样做。

我确信如果您不想自己承担这个问题,那么专门针对游戏和物理的javascript框架或库。我没有处理过它们,但谷歌应该知道。

答案 1 :(得分:1)

您似乎理解了这个问题:场景的离散采样使得高速射弹穿过或错过了应该击中的物体。

解决此问题的天真方法是提高帧速率。这最终变得不可行,因为射弹越快,你需要制作的帧越多。 不推荐。您需要的是一些连续碰撞检测方法。例如,Bullet使用它,并且不会遇到您遇到的问题。

我能想到的最简单的事情是创建一个从current_positionlast_position的圆柱体,其半径与球的半径相同。然后,我会检查与这些气缸的碰撞。这将解决弹丸撞击静止目标的简单情况。对于移动目标,您可以从上面开始做同样的事情(也就是说,您可以将柱面与柱面进行比较)。如果发生碰撞,您需要在碰撞时重新计算碰撞对象的位置,然后查看它们是否实际碰撞。