1

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

任何可能引起我的路径的东西都会被赞成!

我做了这个非常简单的例子。如果两个球的速度都是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。

回答

1

问题是使用球的位置之间的距离来完成碰撞检测,但是如果它们移动得太快,那么它们可能会“跳”到彼此的正上方,并且永远不会足够接近碰撞被检测到。

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

我相信,如果你不想自己做这件事,我肯定存在专门用于游戏和物理的javascript框架或库。我从来没有处理过它们,但谷歌应该知道。

1

您似乎明白这个问题:场景的离散采样会使高速射弹穿过或错过它们应该击中的物体。

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

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