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's current position vs all objects one by one.... } 

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

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

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

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

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

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

 function isCollide(a, b) { return !( ((ay + a.height) < (by)) || (ay > (by + b.height)) || ((ax + a.width) < bx) || (ax > (bx + b.width)) ); } 

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

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

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

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

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

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

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

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

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

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

Отсюда

У 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, в котором вы кластерные объекты иерархически в соответствии с их расстоянием, и вы даете каждому кластеру ограничивающий прямоугольник, который содержит все элементы кластера. Затем вы можете проверить, какие кластеры сталкиваются и не проверять пары Объект, который принадлежит кластерам, которые не сталкиваются / перекрываются.

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

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

Ответ «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 двух разных объектов спрайта

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 } 
Давайте будем гением компьютера.