web-dev-qa-db-fra.com

Javascript: Détection de collision

quelqu'un pourrait-il m'aider à comprendre comment fonctionne la détection de collision dans JS? Je ne peux pas utiliser jQuery ou gameQuery - utilisant déjà un prototype - donc je cherche quelque chose de très simple. Ne demandant pas de solution complète, pointez-moi simplement dans la bonne direction.

Disons qu'il y a:

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

Maintenant, la balle se déplace (dans toutes les directions). "Someobject" (0-X) est déjà prédéfini et il y en a 20 à 60 positionnés aléatoirement comme ceci:

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

Je peux créer un tableau avec des positions "someobject (X)" et tester la collision pendant que la "boule" se déplace ... Quelque chose comme:

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

Mais je suppose que ce serait une solution "noob" et cela semble assez lent. Y a-t-il quelque chose de mieux?

45
jack moore

La première chose à avoir est la fonction réelle qui détectera si vous avez une collision entre la balle et l'objet.

Pour des raisons de performances, il sera intéressant de mettre en œuvre une technique de détection de collision grossière, par exemple rectangles de délimitation , et une plus précise si nécessaire en cas de détection de collision, afin que votre fonction exécute un un peu plus rapide mais en utilisant exactement la même boucle.

Une autre option qui peut aider à augmenter les performances consiste à effectuer un prétraitement avec les objets dont vous disposez. Par exemple, vous pouvez diviser toute la zone en cellules comme un tableau générique et stocker l'objet approprié contenu dans les cellules particulières. Par conséquent, pour détecter la collision, vous détectez les cellules occupées par la balle, récupérez les objets de ces cellules et utilisez votre fonction de détection de collision.

Pour l'accélérer encore plus, vous pouvez implémenter 2d-tree , quadtree or R-tree .

29
Li0liQ

Voici une routine de rectangle englobant très simple. Il s'attend à ce que a et b soient des objets avec les propriétés x, y, width et 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))
    );
}

Pour voir cette fonction en action, voici un codepen gracieusement créé par @ MixerOID .

64
Husky

Vous pouvez essayer jquery-collision . Divulgation complète: je viens d'écrire ceci et de le publier. N'ayant pas trouvé de solution, je l'ai écrit moi-même.

Il vous permet de faire:

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

qui retournera tous les "# someobject0" qui se chevauchent avec "#ball".

22
eruciform

Une version sans jQuery, avec HTMLElements comme entrées

Il s'agit d'une meilleure approche qui vérifie la position réelle des éléments lorsqu'ils sont affichés dans la fenêtre, même s'ils sont absolus, relatifs ou ont été manipulés via des transformations:

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))
    );
}
17
Àlex Garcés

La réponse de "bcm", qui a actuellement 0 votes, est en fait une excellente réponse, sous-estimée. Il utilise de bons vieux Pythagore pour détecter quand les objets sont plus proches que leurs cercles de délimitation combinés. La détection de collision simple utilise souvent la détection de collision rectangulaire, ce qui est bien si vos sprites ont tendance à être bien rectangulaires. S'ils sont circulaires (ou autrement moins que rectangulaires), comme une balle, un astéroïde ou toute autre forme où les coins extrêmes sont généralement transparents, cette routine efficace peut être la plus précise.

Mais pour plus de clarté, voici une version plus complète du code:

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

Où les paramètres à transmettre sont les valeurs x, y et largeur de deux objets Sprite différents

7
J Sprague

Mozilla a un bon article à ce sujet, avec le code ci-dessous

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

Collision rectangulaire

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

Collision de cercle

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

C'est une solution légère que j'ai rencontrée -

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

Les grandes et petites variables sont de 2 objets, (x coord, y coord et w width)

De ici

5
bcm
//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
Fordi

C'est un moyen simple qui est inefficace mais c'est tout à fait raisonnable lorsque vous n'avez besoin de rien de trop complexe ou que vous n'avez pas beaucoup d'objets.

Sinon, il existe de nombreux algorithmes différents, mais la plupart d'entre eux sont assez complexes à mettre en œuvre.

Par exemple, vous pouvez utiliser une approche divide et impera dans laquelle vous regroupez les objets de manière hiérarchique en fonction de leur distance et vous donnez à chaque cluster une boîte englobante qui contient tous les éléments du cluster. Ensuite, vous pouvez vérifiez quels clusters entrent en collision et évitez de vérifier les paires d'objets qui appartiennent à des clusters qui ne se heurtent pas/ne se chevauchent pas.

Sinon, vous pouvez trouver un algorithme générique de partitionnement d'espace pour diviser de manière similaire les objets pour éviter les vérifications inutiles. Ce type d'algorithmes divise la détection de collision en deux phases: une grossière une dans laquelle vous voyez quels objets peuvent entrer en collision et une fine une dans laquelle vous vérifiez efficacement objets uniques. Par exemple, vous pouvez utiliser un QuadTree wikipedia pour entraîner une solution facile.

Jetez un œil à wikipedia page , il peut vous donner quelques indices.

3
Jack

hittest.js; détecte deux collisions d'images png transparentes (pixels). Démo et lien de téléchargement

Code HTML;

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

Fonction Init;

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

var object1HitTest = new HitTest( pngObject1Element );

Utilisation de base;

if( object1HitTest.toObject( pngObject2Element ) ) {
    //Collision detected
}
1
bugra ozden