如何检测发生碰撞的一侧

时间:2019-06-15 02:01:59

标签: javascript html collision-detection game-development

这是我的第一篇文章,所以我试图使我的问题尽可能清楚。我正在做一个游戏,我想改善碰撞检测。这是因为我想检查击打的是哪一侧,并阻止玩家越过它,而无需使用诸如 if(collision(player(敌人,敌人)))的常规内容。player.x =敌人.x-player.w(width ),因为如果玩家与顶部碰撞,就不会将玩家保持在顶部。

在代码中,它检查是否有任何一条陈述为真,然后返回该陈述,但它没有告诉我哪一条陈述等于真实,因此我可以阻止玩家相应移动,如果这样做感。如果您有更有效的碰撞检测方法供我使用,将不胜感激。

我已经尝试过使位置变量等于碰撞到的任何一侧,然后阻止玩家越过它,但是它仅适用于左侧,不会让我的玩家跳过敌人或阻止。

function collision(object1, object2) {

       return !(
            object1.x > object2.x + object2.w  ||
            object1.x + object1.w < object2.x  ||
            object1.y > object2.y + object2.h  ||
            object1.y + object1.h < object2.y 
        )
}

//Only works for the left side

if(collision(player, enemy)) player.x = enemy.x - player.w

我希望它能够告诉我正在撞到哪一侧,然后阻止玩家越过/进入,并使玩家能够在不被按下的情况下位于方块/敌人的顶部左边。

1 个答案:

答案 0 :(得分:0)

您将要计算x和y之间的距离,并使用它们可能沿每个轴碰撞的最小距离来查找两个轴的深度。然后,您可以选择较小的深度并沿该深度移动。这是一个示例:

if(collision(player, enemy)){
    // Most of this stuff would probably be good to keep stored inside the player
    // along side their x and y position. That way it doesn't have to be recalculated
    // every collision check
    var playerHalfW = player.w/2
    var playerHalfH = player.h/2
    var enemyHalfW = enemy.w/2
    var enemyHalfH = enemy.h/2
    var playerCenterX = player.x + player.w/2
    var playerCenterY = player.y + player.h/2
    var enemyCenterX = enemy.x + enemy.w/2
    var enemyCenterY = enemy.y + enemy.h/2

    // Calculate the distance between centers
    var diffX = playerCenterX - enemyCenterX
    var diffY = playerCenterY - enemyCenterY

    // Calculate the minimum distance to separate along X and Y
    var minXDist = playerHalfW + enemyHalfW
    var minYDist = playerHalfH + enemyHalfH

    // Calculate the depth of collision for both the X and Y axis
    var depthX = diffX > 0 ? minXDist - diffX : -minXDist - diffX
    var depthY = diffY > 0 ? minYDist - diffY : -minYDist - diffY

    // Now that you have the depth, you can pick the smaller depth and move
    // along that axis.
    if(depthX != 0 && depthY != 0){
      if(Math.abs(depthX) < Math.abs(depthY)){
        // Collision along the X axis. React accordingly
        if(depthX > 0){
            // Left side collision
        }
        else{
            // Right side collision
        }
      }
      else{
        // Collision along the Y axis.
        if(depthY > 0){
           // Top side collision
        }
        else{
           // Bottom side collision
        }
      }
    }
  }

工作示例

这是一个可操作的示例。使用箭头键来移动播放器。

player = {
  x: 9,
  y: 50,
  w: 100,
  h: 100
}
enemy = {
  x: 100,
  y: 100,
  w: 100,
  h: 100
}
output = document.getElementById("collisionType");
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d")

function collision(object1, object2) {
  return !(
    object1.x > object2.x + object2.w ||
    object1.x + object1.w < object2.x ||
    object1.y > object2.y + object2.h ||
    object1.y + object1.h < object2.y
  )
}

function draw() {
  ctx.clearRect(0, 0, 400, 400)
  ctx.lineWidth = "5"
  ctx.beginPath();
  ctx.strokeStyle = "red";
  ctx.rect(player.x, player.y, player.w, player.h);
  ctx.stroke();

  ctx.beginPath();
  ctx.strokeStyle = "blue";
  ctx.rect(enemy.x, enemy.y, enemy.w, enemy.h);
  ctx.stroke();

}

function handleCollision() {
  if (collision(player, enemy)) {
    var playerHalfW = player.w / 2
    var playerHalfH = player.h / 2
    var enemyHalfW = enemy.w / 2
    var enemyHalfH = enemy.h / 2
    var playerCenterX = player.x + player.w / 2
    var playerCenterY = player.y + player.h / 2
    var enemyCenterX = enemy.x + enemy.w / 2
    var enemyCenterY = enemy.y + enemy.h / 2

    // Calculate the distance between centers
    var diffX = playerCenterX - enemyCenterX
    var diffY = playerCenterY - enemyCenterY

    // Calculate the minimum distance to separate along X and Y
    var minXDist = playerHalfW + enemyHalfW
    var minYDist = playerHalfH + enemyHalfH

    // Calculate the depth of collision for both the X and Y axis
    var depthX = diffX > 0 ? minXDist - diffX : -minXDist - diffX
    var depthY = diffY > 0 ? minYDist - diffY : -minYDist - diffY

    // Now that you have the depth, you can pick the smaller depth and move
    // along that axis.
    if (depthX != 0 && depthY != 0) {
      if (Math.abs(depthX) < Math.abs(depthY)) {
        // Collision along the X axis. React accordingly
        if (depthX > 0) {
          output.innerHTML = "left side collision"
        } else {
          output.innerHTML = "right side collision"
        }
      } else {
        // Collision along the Y axis.
        if (depthY > 0) {
          output.innerHTML = "top side collision"
        } else {
          output.innerHTML = "bottom side collision"
        }
      }
    }
  } else {
    output.innerHTML = "No collision"
  }
}

keyStates = []

function handleKeys() {
  if (keyStates[39]) {
    player.x += 2 //Move right
  } else if (keyStates[37]) {
    player.x -= 2 //Move left
  }
  if (keyStates[38]) {
    player.y -= 2 //Move up
  }
  if (keyStates[40]) {
    player.y += 2 //Move down
  }
}

function main() {
  handleKeys();
  draw();
  handleCollision();
  window.requestAnimationFrame(main);
}

window.onkeydown = function(e) {
  keyStates[e.keyCode] = true
}

window.onkeyup = function(e) {
  keyStates[e.keyCode] = false
}

main();
<h2 id="collisionType"></h2>
<canvas id="canvas" width='300' height='300'></canvas>

对碰撞做出反应

现在您知道碰撞发生的那一面,决定如何做出反应应该很简单。它将与您当前在左侧所做的操作非常相似,只需翻转一些符号并更改轴即可。

其他注意事项

  • 您可能需要考虑玩家的速度(如果有),否则检测可能会失败。
    • 如果玩家的速度过高,它可能会“隧道”穿过敌人,并且不会检测到碰撞。
    • 如果碰撞后速度没有停止,玩家的动作也会显得抖动
  • 您的对象可以旋转或具有4个以上的边吗?如果是这样,您可能要使用如下所述的另一种方法。

Here's a good answer to another post that talks in depth about collision engines

其他方法

对于其他碰撞检测方法,还有很多,但我想到的是Separating Axis Theorem,它比您拥有的要复杂一些,但可以使用更复杂的凸形和旋转。它还告诉您解决碰撞所需的方向和距离。 Here's a site包含互动示例,并深入探讨了该主题。它似乎没有提供完整的实现,但是可以在其他地方找到。