2010-03-13 4 views
38

Может кто-то, пожалуйста, помогите мне понять, как работает обнаружение столкновения в JS? Я не могу использовать jQuery или gameQuery - уже используя прототип - так что я ищу что-то очень простое. Не прося полного решения, просто укажите мне в правильном направлении.Javascript: обнаружение столкновения

Допустим есть:

<div id="ball"></div> 
and 
<div id="someobject0"></div> 

Теперь мяч движется (в любом направлении). «SomeObject» (0-X) уже предопределены и есть 20-60 из них случайным образом расположены так:

#someobject {position: absolute; top: RNDpx; left: RNDpx;} 

Я могу создать массив с «SomeObject (X)» позиции и тест столкновения в то время как «мяч» движется ... что-то вроде:

for(var c=0; c<objposArray.length; c++){ 
........ and code to check ball's current position vs all objects one by one.... 
} 

Но я думаю, что это будет «нуб» решение, и это выглядит довольно медленно. Есть ли что-нибудь лучше?

ответ

27

Первое, что нужно сделать - это фактическая функция, которая будет определять, есть ли у вас столкновение между мячом и объектом.

Для достижения наилучшей производительности будет реализовано некоторая грубая техника обнаружения столкновений, например, bounding rectangles, и более точная, если это необходимо, в случае обнаружения столкновения, так что ваша функция будет работать немного быстрее, но используя точно такой же цикл.

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

Чтобы ускорить его, вы можете использовать 2d-tree, quadtree или R-tree.

3

Это простой способ, который неэффективен, но вполне разумно, когда вам не нужно что-либо слишком сложное или у вас не так много объектов.

В противном случае существует множество различных алгоритмов, но большинство из них довольно сложно реализовать.

Например, вы можете использовать разделяй и властвуй подход, в котором вы группироваться объекты иерархически в соответствии с их расстоянием и вы даете каждому кластеру прямоугольник, содержащий все элементы на cluster.Then вы можете проверить, какие кластеры сталкиваются и не проверяют пары объектов, которые принадлежат кластерам, которые не сталкиваются/перекрываются.

В противном случае вы можете найти общий алгоритм разбиения пространства, чтобы разделить объекты таким образом, чтобы избежать ненужных проверок. Эти алгоритмы делят обнаружение столкновения в два этапа: a грубый, в котором вы видите, какие объекты могут сталкиваться, и штраф, в котором вы эффективно проверяете отдельные объекты. Например вы можете использовать квадрадереваwikipedia для тренировки легкого решения ..

Посмотрите википедию page, он может дать вам несколько советов.

4
//Off the cuff, Prototype style. 
//Note, this is not optimal; there should be some basic partitioning and caching going on. 
(function() { 
    var elements = []; 
    Element.register = function (element) { 
     for (var i=0; i<elements.length; i++) { 
      if (elements[i]==element) break; 
     } 
     elements.push(element); 
     if (arguments.length>1) 
      for (var i=0; i<arguments.length; i++) 
       Element.register(arguments[i]); 
    }; 
    Element.collide = function() { 
     for (var outer=0; outer < elements.length; outer++) { 
      var e1 = Object.extend( 
       $(elements[outer]).positionedOffset(), 
       $(elements[outer]).getDimensions() 
      ); 
      for (var inner=outer; inner<elements.length; innter++) { 
       var e2 = Object.extend( 
        $(elements[inner]).positionedOffset(), 
        $(elements[inner]).getDimensions() 
       ); 
       if (  
        (e1.left+e1.width)>=e2.left && e1.left<=(e2.left+e2.width) && 
        (e1.top+e1.height)>=e2.top && e1.top<=(e2.top+e2.height) 
       ) { 
        $(elements[inner]).fire(':collision', {element: $(elements[outer])}); 
        $(elements[outer]).fire(':collision', {element: $(elements[inner])}); 
       } 
      } 
     } 
    }; 
})(); 

//Usage: 
Element.register(myElementA); 
Element.register(myElementB); 
$(myElementA).observe(':collision', function (ev) { 
    console.log('Damn, '+ev.memo.element+', that hurt!'); 
}); 
//detect collisions every 100ms 
setInterval(Element.collide, 100); 
4

Это легкое решением я сталкивался -

function E() { // check collision 
      S = X - x; 
      D = Y - y; 
      F = w + W; 
      return (S * S + D * D <= F * F) 
     } 

Большой и малые переменные 2 объектов, (х коорда, у коорда и ш ширина)

От here

+1

Насколько я могу судить, t его функция предполагает, что объекты квадратные (или даже круговые?). Также кажется менее читаемым, что код Husky выше, и если этого было недостаточно, он также выглядит медленнее (см .: http://jsperf.com/simple-collision-detection) – Roccivic

19

Вы можете попробовать jquery-collision. Полное раскрытие: я только что написал это и выпустил его. Не нашел решения, поэтому я написал его сам.

Это позволяет сделать:

var hit_list = $("#ball").collision("#someobject0"); 

который будет возвращать все "# someobject0" 'S, которые перекрываются с "#ball".

+6

OP говорит, что они не могут использовать JQuery. Тем не менее приятно, хотя, помог мне в собственном решении. – Irwin

+0

@irwin - хм, я собирался «не использовать jquery - используя прототип», как в, я уверен, что вы действительно можете их смешивать. Я не пробовал, правда, честно. рад, что это тебе помогло! попробуйте jquery-draggable-collision, тоже! :-) – eruciform

+1

jQuery была также моей чашкой чая - хорошее шоу, еще +1. –

49

Вот очень простая рутина ограниченного прямоугольника. Он ожидает, как a и b быть объекты с x, y, width и height свойства:

function isCollide(a, b) { 
    return !(
     ((a.y + a.height) < (b.y)) || 
     (a.y > (b.y + b.height)) || 
     ((a.x + a.width) < b.x) || 
     (a.x > (b.x + b.width)) 
    ); 
} 

Чтобы увидеть эту функцию в действии, here's a codepen любезно сделанное @MixerOID.

+2

Lovely! Сэкономил мне еще 10 операций .. –

+0

Помогло мне много, спасибо :) –

+0

Может кто-нибудь уточнить, что я делаю не так с этим? Он всегда возвращает true независимо от «столкновения» http://jsbin.com/akeSeDI/2/edit – KryptoniteDove

1

hittest.js; обнаружение двух прозрачных png изображений (пикселей). Demo and download link

HTML код;

<img id="png-object-1" src="images/object1.png" /> 
<img id="png-object-2" src="images/object2.png" /> 

Функция запуска;

var pngObject1Element = document.getElementById("png-object-1"); 
var pngObject2Element = document.getElementById("png-object-2"); 

var object1HitTest = new HitTest(pngObject1Element); 

Основное использование;

if(object1HitTest.toObject(pngObject2Element)) { 
    //Collision detected 
} 
5

Ответ «bcm's», в котором 0 голосов на данный момент, на самом деле отличный, недооцененный ответ. Он использует старые добрые Пифагоры, чтобы обнаружить, когда объекты ближе, чем их объединенные ограничивающие круги. Простое обнаружение столкновений часто использует прямоугольное обнаружение столкновений, что прекрасно, если ваши спрайты имеют тенденцию быть, ну, прямоугольными. Если они являются круглыми (или иначе менее прямоугольными), такими как шарик, астероид или любая другая форма, где крайние углы обычно прозрачны, вы можете обнаружить, что эта эффективная рутина является самой точной.

Но для ясности, здесь более полно реализуется вариант кода:

function doCollide(x1, y1, w1, x2, y2, w2) { 
    var xd = x1 - x2; 
    var yd = y1 - y2; 
    var wt = w2 + w1; 
    return (xd * xd + yd * yd <= wt * wt); 
} 

Где параметры проходить в являются х, у и ширина значения двух разных спрайтов объектов

4

Mozilla имеет хорошую статью по этому вопросу, с кодом, показанный ниже

https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection

Rectangle столкновения

if (rect1.x < rect2.x + rect2.width && 
    rect1.x + rect1.width > rect2.x && 
    rect1.y < rect2.y + rect2.height && 
    rect1.height + rect1.y > rect2.y) { 
    // collision detected! 
} 

Круг столкновение

if (distance < circle1.radius + circle2.radius) { 
    // collision detected! 
} 
8

A Исполнение без JQuery, с HTMLElements в качестве входных данных

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

function isCollide(a, b) { 
    var aRect = a.getBoundingClientRect(); 
    var bRect = b.getBoundingClientRect(); 

    return !(
     ((aRect.top + aRect.height) < (bRect.top)) || 
     (aRect.top > (bRect.top + bRect.height)) || 
     ((aRect.left + aRect.width) < bRect.left) || 
     (aRect.left > (bRect.left + bRect.width)) 
    ); 
} 
+0

На самом деле это должен быть единственный ответ на вышеупомянутый вопрос. Главный ответ требует jQuery – eljefedelrodeodeljefe

+0

Лучший ответ ... –

Смежные вопросы