Обнаружение столкновений Javascript canvas

#javascript #canvas #collision-detection

#javascript #canvas #обнаружение столкновений

Вопрос:

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

У меня есть глобальный массив с именем blockList , который содержит все поля, отображаемые на холсте. Это выглядит так:

 var blockList = [[50, 400, 100, 100]];
  

И они отображаются на холсте следующим образом:

 c.fillRect(blockList[0][0], blockList[0][1], blockList[0][2], blockList[0][3]);
  

У меня также есть объект player, у которого есть метод обновления и метод рисования. Обновление устанавливает положение игрока на основе ввода с клавиатуры и т.д., А draw используется основным игровым циклом для вывода игрока на холст. Проигрыватель рисуется следующим образом:

 this.draw = function(timestamp) {
        if(this.state == "idle") {
            c.drawImage(this.idleSprite, this.idleSprite.frameWidth * this.idleSprite.frameCount, 0, this.idleSprite.frameWidth, this.idleSprite.frameHeight, this.xpos, this.ypos, this.idleSprite.frameWidth, this.idleSprite.frameHeight);
            if(timestamp - this.lastDraw > this.idleSprite.updateInterval) {
                this.lastDraw = timestamp;
                if(this.idleSprite.frameCount < this.idleSprite.frames - 1) { this.idleSprite.frameCount  ; } else { this.idleSprite.frameCount = 0; }
            }
        } else if(this.state == "running") {
            var height = 0;
            if(this.facing == "left") { height = 37; }
            c.drawImage(this.runningSprite, this.runningSprite.frameWidth * this.runningSprite.frameCount, height, this.runningSprite.frameWidth, this.runningSprite.frameHeight, this.xpos, this.ypos, this.runningSprite.frameWidth, this.runningSprite.frameHeight);
            if(timestamp - this.lastDraw > this.runningSprite.updateInterval) {
                this.lastDraw = timestamp;
                if(this.runningSprite.frameCount < this.runningSprite.frames - 1) { this.runningSprite.frameCount  ; } else { this.runningSprite.frameCount = 0; }
            }
        }
    }
  

Теперь у проигрывателя есть определенные свойства, которые являются player.xpos , player.ypos , player.width player.height . Те же свойства существуют для блоков. Итак, у меня есть все, что мне нужно для обнаружения столкновений, я просто понятия не имею, как это сделать. Я пытался делать такие вещи, как:

 if(player.x > blockList[0][0] amp;amp; player.y > blockList[0][1])
  

но это далеко от совершенства или воспроизводимости.

Кто-нибудь знает о простом методе или функции, которые могли бы возвращать true или false в зависимости от того, сталкиваются ли два объекта?

Ответ №1:

Я использую следующую функцию для обнаружения столкновений между двумя прямоугольниками:

 rect_collision = function(x1, y1, size1, x2, y2, size2) {
  var bottom1, bottom2, left1, left2, right1, right2, top1, top2;
  left1 = x1 - size1;
  right1 = x1   size1;
  top1 = y1 - size1;
  bottom1 = y1   size1;
  left2 = x2 - size2;
  right2 = x2   size2;
  top2 = y2 - size2;
  bottom2 = y2   size2;
  return !(left1 > right2 || left2 > right1 || top1 > bottom2 || top2 > bottom1);
};
  

Это определяет, перекрываются ли два квадрата с центром в (x1, y1) и (x2, y2) и длинами сторон 2*size1 и 2*size2 , соответственно. Должно быть достаточно легко изменить определения left1 , right1 и т.д. для работы с общими прямоугольниками, а не только с квадратами, и для использования другого формата данных.

В частности, left1 это левая сторона первого квадрата, right1 правая сторона и т.д. Обратите внимание, что в моей системе координат ось y инвертирована ( top1 < bottom1 ).

Ответ №2:

Вы просто хотите знать, перекрываются ли два прямоугольника?

Вот для вас пуленепробиваемая функция:

 // returns true if there is any overlap
// params: x,y,w,h of two rectangles
function intersects(x1, y1, w1, h1, x2, y2, w2, h2) {
  if (w2 !== Infinity amp;amp; w1 !== Infinity) {
    w2  = x2;
    w1  = x1;
    if (isNaN(w1) || isNaN(w2) || x2 > w1 || x1 > w2) return false;
  }
  if (y2 !== Infinity amp;amp; h1 !== Infinity) {
    h2  = y2;
    h1  = y1;
    if (isNaN(h1) || isNaN(y2) || y2 > h1 || y1 > h2) return false;
  }
  return true;
}
  

Если ваша программа может быть уверена, что числа всегда будут конечными, вы можете использовать более простую версию:

 // returns true if there is any overlap
// params: x,y,w,h of two rectangles
function intersects(x1, y1, w1, h1, x2, y2, w2, h2) {
    w2  = x2;
    w1  = x1;
    if (x2 > w1 || x1 > w2) return false;
    h2  = y2;
    h1  = y1;
    if (y2 > h1 || y1 > h2) return false;
  return true;
}
  

Что он делает, так это находит, где находятся правая и нижняя стороны двух прямоугольников, затем он видит, начинается ли второй прямоугольник за пределами первого или первый начинается за пределами второго.

Если один из прямоугольников начинается после окончания другого, то столкновения нет. В противном случае должно произойти столкновение.

Ответ №3:

На мой взгляд, я не фанат функций, которые требуют много параметров.

Вот как я бы это сделал :

 function collisionCheckRectRect(rectOne, rectTwo){

    var x1=rectOne.x, y1 = rectOne.y, height1 = rectOne.height, width1 = rectOne.width;
    var x2=rectTwo.x, y2 = rectTwo.y, height2 = rectTwo.height, width2 = rectTwo.width; 

    return x1 < x2 width2 amp;amp; x2 < x1 width1 amp;amp; y1 < y2 height2 amp;amp; y2 < y1 height1;
}