Javascript: обнаружение столкновений - PullRequest
44 голосов
/ 14 марта 2010

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

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

<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....
}

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

Ответы [ 10 ]

62 голосов
/ 05 сентября 2011

Вот очень простая процедура ограничивающего прямоугольника. Он ожидает, что и 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))
    );
}

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

29 голосов
/ 14 марта 2010

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

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

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

Для еще большего ускорения вы можете реализовать 2d-дерево , quadtree или R-tree .

21 голосов
/ 19 мая 2011

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

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

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

, который вернет все "# someobject0", которые перекрываются с "#ball".

15 голосов
/ 13 марта 2016

Версия без 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))
    );
}
7 голосов
/ 11 января 2014

Ответ «bcm», который имеет 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);
} 

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

5 голосов
/ 29 июня 2015

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

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

Прямоугольное столкновение

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!
}
5 голосов
/ 30 марта 2011

Это легкое решение, с которым я столкнулся -

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

Большая и маленькая переменные имеют 2 объекта (координаты x, координаты y и ширина w)

С здесь

4 голосов
/ 30 августа 2010
//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);
3 голосов
/ 14 марта 2010

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

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

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

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

Взгляните на википедию страница , она может дать вам несколько советов.

1 голос
/ 23 августа 2013

hittest.js; обнаружение двух прозрачных png изображений (пикселей) столкновения. Демо и ссылка для скачивания

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
}
...