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

Может кто-нибудь, пожалуйста, помогите мне понять, как работает обнаружение столкновения в 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 current position vs all objects one by one....
}

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

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

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

Это позволяет:

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

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

Ответ 4

Версия без 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))
    );
}

Ответ 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);
} 

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

Ответ 6

//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);

Ответ 7

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

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

Большая и малая переменные состоят из двух объектов, (координата x, координата y и ширина w)

Из здесь

Ответ 8

В 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!
}

Ответ 9

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

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

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

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

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

Ответ 10

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

Код HTML;

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

Функция Init;

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
}