Le système de détection de collision Javascript n'ignore pas les collisions bloquées

J'ai des problèmes lors d'un développement de mini-jeux utilisant EaselJS avec mon système de détection de collision et j'ai besoin de l'aide de quelqu'un. Le problème se produit lorsque le héros (un bitmap en cercle) collisionne avec un objet et qu'il y a un autre objet derrière le premier, le héros collabore avec les deux objets, même si la deuxième collision est bloquée. Voici une explication d'image:

La cause du problème est vraiment simple, même si le problème lui-même n'est pas le cas:

Ce système de détection de collision est basé sur la position future du cercle (et non sur sa position réelle), alors si la prochaine position du cercle coupe un rectangle, elle rebondira. Le problème est que, si la position future coupe deux rectangles, le cercle rebondit dans les deux rectangles – même si le mouvement du cercle réel est bloqué par un autre rectangle et il ne peut pas atteindre le second.

Mise à jour: Notez que ce problème ne se produit que lorsque la flèche vers le haut est en attente en raison de l'ordre de création correcte.


Voici le code javascript pertinent:

rects.forEach(function (rect) { // Affect all rects // Collision detection: // (This MUST BE after every change in xvel/yvel) // Next circle position calculation: var nextposx = circle.x + event.delta / 1000 * xvel * 20, nextposy = circle.y + event.delta / 1000 * yvel * 20; // Collision between objects (Rect and Circle): if (nextposy + height(circle) > rect.y && nextposx + width(circle) > rect.x && nextposx < rect.x + rect.width && nextposy < rect.y + rect.height) { if (circle.y + height(circle) < rect.y) { cls("top"); } if (circle.x + width(circle) < rect.x) { cls("left"); } if (circle.x > rect.x + rect.width) { cls("right"); } if (circle.y > rect.y + rect.height) { cls("bottom"); } } // Stage collision: if (nextposy < 0) { // Collided with TOP of stage. Trust me. cls("bottom"); // Inverted collision side is proposital! } if (nextposx < 0) { cls("right"); } if (nextposx + width(circle) > stage.canvas.width) { cls("left"); } if (nextposy + height(circle) > stage.canvas.height) { cls("top"); } }); 

JSFiddle

Vous devez traiter les collisions horizontales et verticales de manière indépendante.

J'ai apporté quelques modifications mineures à votre JS-violon: http://jsfiddle.net/Kf6cv/1/ il devrait fonctionner maintenant, ce que j'ai fait, c'est que j'ai divisé votre vérification unique en deux:

 if (nextposy + height(circle) > rect.y && circle.x + width(circle) > rect.x && circle.x < rect.x + rect.width && nextposy < rect.y + rect.height) { if (circle.y + height(circle) < rect.y) { cls("top"); } if (circle.y > rect.y + rect.height) { cls("bottom"); } } if (nextposx + width(circle) > rect.x && nextposx < rect.x + rect.width && circle.y + height(circle) > rect.y && circle.y < rect.y + rect.height) { if (circle.x + width(circle) < rect.x) { cls("left"); } if (circle.x > rect.x + rect.width) { cls("right"); } } 

La raison en est que si vous vérifiez les deux directions à la fois, cela empêchera le mouvement (ou le fera rebondir) pour les deux directions (comme la figure rouge dans votre image) – même si elle pourrait se déplacer dans une seule direction. L'ordre de vérification horizontal / vertical n'a généralement pas d'importance, il est généralement important que votre "héros" approche l'autre objet 100% bord-à-bord. Mais ce que vous pourriez, c'est d'abord vérifier la direction avec la vitesse supérieure, alors si | velX | > | VelY | Ensuite, vous vérifiez d'abord une collision horizontale.

Aussi, je dirais qu'il est prudent d'appliquer le nouveau poste directement après le contrôle, maintenant il fait deux contrôles indépendants puis applique le mouvement des deux directions – Je ne suis pas sûr de cela, mais je peux imaginer que cela pourrait conduire à Quelques problèmes plus tard.

J'ai trouvé une solution pour cette question sans compromettre les collisions de bord-à-bord!

D'abord, j'ai séparé la boucle forEach en deux pour forEach s: la première manipule les collisions côte à côte et les collisions de bord à bord des deuxièmes manettes, car de cette façon, la réaction côte à côte est appliquée à tous les objets d'abord, Et vérifie les collisions de bord à bord restantes. Ensuite, j'ai changé la façon dont la détection de collision est faite dans son noyau, en utilisant une comparaison entre les positions de la dernière et de la dernière image (au lieu d'être en cours et future en utilisant une estimation).

Voici le code final pertinent:

 function colhndlr(obj1, obj2array){ var sidecollided = false; // Side-to-side collision handler: obj2array.forEach(function(obj2){ // Top side: if(obj1.ypast + obj1.height < obj2.y && obj1.y + obj1.height >= obj2.y && // Checks if past position X was correct: obj1.xpast + obj1.width >= obj2.x && obj1.xpast <= obj2.x + obj2.width){ // Collided with top } // Left side: if(obj2.clsdir > 2 && // Checks if object is side-collideable obj1.xpast + obj1.width < obj2.x && obj1.x + obj1.width >= obj2.x && // Checks if past position Y was correct: obj1.ypast + obj1.height >= obj2.y && obj1.ypast <= obj2.y + obj2.height){ // Collided with left } // Right side: if(obj2.clsdir > 2 && // Checks if object is side-collideable obj1.xpast > obj2.x + obj2.width && obj1.x <= obj2.x + obj2.width && // Checks if past position Y was correct: obj1.ypast + obj1.height >= obj2.y && obj1.ypast <= obj2.y + obj2.height){ // Collided with right } // Bottom side: if(obj2.clsdir == 4 && // Checks if object is bottom-collideable obj1.ypast > obj2.y + obj2.height && obj1.y <= obj2.y + obj2.height && // Checks if past position X was correct: obj1.xpast + obj1.width >= obj2.x && obj1.xpast <= obj2.x + obj2.width){ // Collided with bottom } }); /* Now that every side-to-side collision change was made, the edge-to-edge collision handler will not detect blocked collisions. */ if(!sidecollided){ // Edge-to-edge collision handler: obj2array.forEach(function(obj2){ if(obj2.clsdir != 1){ // If it's 1 then there's no need for edge collisions // Top left edge: if(obj1.ypast + obj1.height < obj2.y && obj1.xpast + obj1.width < obj2.x && obj1.y + obj1.height >= obj2.y && obj1.x + obj1.width >= obj2.x){ // Collided with top left edge } // Top right edge: if(obj1.ypast + obj1.height < obj2.y && obj1.xpast > obj2.x + obj2.width && obj1.y + obj1.height >= obj2.y && obj1.x <= obj2.x + obj2.width){ // Collided with top right edge } // Bottom right edge: if(obj2.clsdir == 4 && // Checks if object is bottom collideable obj1.ypast > obj2.y + obj2.height && obj1.xpast > obj2.x + obj2.width && obj1.y <= obj2.y + obj2.height && obj1.x <= obj2.x + obj2.width){ // Collided with bottom right edge } // Bottom left edge: if(obj2.clsdir == 4 && // Checks if object is bottom collideable obj1.ypast > obj2.y + obj2.height && obj1.xpast + obj1.width < obj2.x && obj1.y <= obj2.y + obj2.height && obj1.x + obj1.width >= obj2.x){ // Collided with bottom left edge } } }); } } 

JSFiddle