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的圓柱體,其半徑與球的半徑相同。然後,我會檢查與這些氣瓶的碰撞。這將解決射彈擊中靜止目標的簡單情況。對於移動目標,你可以從上面的相同的事情開始(也就是說,你會比較氣瓶與氣瓶)。如果發生碰撞,您需要在碰撞時重新計算碰撞物體的位置,然後查看它們是否實際碰撞。