Как определить сторону, на которой произошло столкновение - PullRequest
2 голосов
/ 15 июня 2019

Это мой первый пост, поэтому я пытаюсь сделать мою проблему как можно более понятной.Я делаю игру и хочу улучшить обнаружение столкновений.Это потому, что я хочу проверить, на какую сторону наносят удары, и не дать игроку пройти мимо него, не используя что-то общее, например if (столкновение (игрок, враг)) player.x = врага.x - player.w (ширина)) потому что, если игрок столкнется с вершиной, это не удержит игрока на вершине.

В коде он проверяет, является ли какое-либо из утверждений верным, и затем возвращает его, но это не так.Не говорите мне, какое утверждение было тем, которое было равно истине, чтобы я мог помешать игроку двигаться соответствующим образом, если это имеет смысл.Если у вас есть более эффективное обнаружение столкновений, я буду очень признателен.

Я уже пытался сделать переменную позиции равной любой столкнувшейся стороне, а затем не дать игроку пройти мимо нее, но это работает только для левой стороны и не позволит моему игроку прыгнутьнад врагом или блоком.

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 голосов
/ 15 июня 2019

Вы захотите рассчитать расстояние между 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 сторон?Если это так, вы, вероятно, захотите использовать другой метод, как описано ниже.

Вот хороший ответ на другой пост, в котором подробно говорится о двигателях столкновений

Другие методы

Что касается других методов обнаружения столкновений, то есть немало, но на ум приходит Теорема разделения осей , которая немного сложнее, чем у вас, но будет работать сболее сложные выпуклые формы и вращение.Он также сообщает вам направление и расстояние, необходимое для перемещения, чтобы разрешить столкновение. Вот сайт , на котором есть интерактивные примеры и он углублен в предметную область.Это не дает полной реализации, но их можно найти в других местах.

...