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

I можно создать массив с позициями" someobject(X) "и протестировать столкновение во время движения" шара"... Что-то вроде:

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

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

10 ответов


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

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

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

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


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

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


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

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

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

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


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

ответ "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 и width двух разных объектов sprite


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

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

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

большие и малые переменные имеют 2 объекта (X coord, y coord и W width)

С здесь


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

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

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

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

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

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


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
}