J'ai implémenté une routine de détection de collision simple en utilisant des AABB entre mon sprite de jeu principal et diverses plates-formes (veuillez voir le code ci-dessous). Cela fonctionne très bien, mais j'introduis maintenant la gravité pour faire tomber mon personnage et cela a montré des problèmes avec ma routine de CD.
Je pense que le nœud du problème est que ma routine de CD fait reculer le sprite le long de l'axe dans lequel il a pénétré le moins dans l'autre sprite. Donc, s'il est plus dans l'axe Y que dans le X, il le fera reculer le long de l'axe X.
Cependant, maintenant mon sprite (héros) tombe maintenant à un taux de 30 pixels par image (c'est un écran de 1504 de haut - j'en ai besoin pour tomber aussi vite car je veux essayer de simuler la gravité `` normale '', tout plus lent a juste l'air bizarre) ) Je reçois ces problèmes. Je vais essayer de montrer ce qui se passe (et ce que je pense en être la cause - bien que je ne sois pas sûr) avec quelques images: (Code ci-dessous les images).
J'apprécierais quelques suggestions sur la façon de contourner ce problème.
Pour plus de précision, dans l'image ci-dessus à droite, quand je dis que la position est corrigée `` incorrectement '', c'est peut-être un peu trompeur. Le code lui-même fonctionne correctement pour la façon dont il est écrit, ou autrement dit, l'algorithme lui-même s'il se comporte comme je m'y attendrais, mais je dois changer son comportement pour éviter que ce problème ne se produise, j'espère que cela clarifie mes commentaires dans l'image .
Mon code
public boolean heroWithPlatforms(){
//Set Hero center for this frame
heroCenterX = hero.xScreen+(hero.quadWidth/2);
heroCenterY = hero.yScreen+(hero.quadHeight/2);
//Iterate through all platforms to check for collisions
for(x=0;x<platformCoords.length;x+=2){
//Set platform Center for this iteration
platformCenterX = platformCoords[x]+(platforms.quadWidth/2);
platformCenterY = platformCoords[x+1]+(platforms.quadHeight/2);
// the Dif variables are the difference (absolute value)
// of the center of the two sprites being compared (one for X axis difference
//and on for the Y axis difference)
difX = Math.abs(heroCenterX-platformCenterX);
difY = Math.abs(heroCenterY-platformCenterY);
//If 1
//Check the difference between the 2 centers and compare it to the vector (the sum of
//the two sprite's widths/2. If it is smaller, then the sprites are pverlapping along
//the X axis and we can now proceed to check the Y axis
if (difX<vecXPlatform){
//If 2
//Same as above but for the Y axis, if this is also true, then we have a collision
if(difY<vecYPlatform){
//we now know sprites are colliding, so we now need to know exactly by how much
//penX will hold the value equal to the amount one sprite (hero, in this case)
//has overlapped with the other sprite (the platform)
penX = vecXPlatform-difX;
penY = vecYPlatform-difY;
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite
//back on the X Axis
if (penX < penY){hero.xScreen-=penX*(heroCenterX-platformCenterX>=0 ? -1 : 1);}
//Sprite has penetrated into the other, mostly in the X asis, so move sprite
//back on the Y Axis
else if (penY < penX) {hero.yScreen-=penY*(heroCenterY-platformCenterY>=0 ? -1 : 1);}
return true;
}//Close 'If' 2
} //Close 'If' 1
}
//Otherwise, no collision
return false;
}
la source
//One sprite has penetrated into the other, mostly in the Y axis, so move sprite //back on the X Axis
Réponses:
lors de mes expériences avec HTML5 canvas et AABB, j'ai trouvé exactement ce que vous vivez. Cela s'est produit lorsque j'ai tenté de créer une plate-forme à partir de boîtes adjacentes de 32x32 pixels.
Solutions que j'ai essayées par ordre de mes préférences personnelles
1 - Diviser les mouvements par axe
Mon actuel, et je pense que je continuerai mon jeu avec. Mais assurez-vous de vérifier ce que j'appelle Phantom AABB si vous avez besoin d'une solution rapide sans avoir à modifier beaucoup votre conception actuelle.
Mon monde de jeu a une physique très simple. Il n'y a pas (encore) de concept de forces, seulement de déplacement. La gravité est un déplacement constant vers le bas. Pas d'accélération (pour l'instant).
Les déplacements sont appliqués par axe. Et en cela consiste cette première solution.
Chaque cadre de jeu:
move_x est défini en fonction du traitement d'entrée, si ce n'est pas la touche fléchée enfoncée, alors move_x = 0. Les valeurs inférieures à zéro signifient gauche, sinon à droite.
Traitez tous les autres sprites mobiles et décidez des valeurs de leur composant "moves", ils ont aussi le composant "moves".
Remarque: après la détection et la réponse aux collisions, le composant de déplacement doit être défini sur zéro, les propriétés x et y, car au prochain tick, la gravité sera à nouveau ajoutée. Si vous ne réinitialisez pas, vous vous terminerez avec un moves.y de deux fois la gravité souhaitée, et dans la coche suivante, ce sera trois fois.
Entrez ensuite le code d'application de déplacement et de détection de collision.
Le composant moves n'est pas vraiment la position actuelle du sprite. Le sprite n'a pas encore bougé même si moves.x ou y a changé. Le composant "moves" est un moyen de sauvegarder le déplacement à appliquer à la position du sprite dans la bonne partie de la boucle de jeu.
Traitez d'abord l'axe y (moves.y). Appliquez moves.y à sprite.position.y. Ensuite, appliquez la méthode AABB pour voir si l'image-objet en cours de traitement chevauche une plateforme AABB (vous avez déjà compris comment le faire). S'il se chevauche, déplacez le sprite dans l'axe y en appliquant la pénétration.y. Oui, contrairement à mon autre réponse, maintenant cette méthode de mouvement évolué ignore le penetration.x, pour cette étape du processus. Et nous utilisons le penetration.y même s'il est supérieur à penetration.x, nous ne le vérifions même pas.
Traitez ensuite l'axe x (moves.x). Appliquez moves.x à sprite.position.x. Et faites le contraire qu'auparavant. Cette fois, vous ne déplacerez le sprite que dans l'axe horizontal en appliquant penetration.x. Comme auparavant, peu importe si la pénétration.x est inférieure ou supérieure à la pénétration.y.
Le concept ici est que si le sprite ne bouge pas, et n'était initialement pas en collision, il restera comme ça dans l'image suivante (sprite.moves.x et y sont nuls). Le cas spécial où un autre objet de jeu se téléporte comme par magie à une position qui chevauche le début du sprite est quelque chose que je traiterai plus tard.
Quand un sprite commence à bouger. S'il ne se déplace que dans l'axe x, alors nous ne sommes intéressés que s'il pénètre quelque chose par la gauche ou la droite. Comme le sprite ne se déplace pas vers le bas, et nous le savons parce que la propriété y du composant moves est nulle, nous ne vérifions même pas la valeur calculée pour la pénétration.y, nous ne voyons que la pénétration.x. Si la pénétration existe dans l'axe de mouvement, nous appliquons la correction, sinon nous laissons simplement le sprite bouger.
Qu'en est-il du déplacement diagonal, pas nécessairement dans un angle de 45 degrés?
Le système proposé peut le gérer. Il vous suffit de traiter chaque axe séparément. Pendant votre simulation. Différentes forces sont appliquées au sprite. Même si votre moteur ne comprend pas encore les forces. Ces forces se traduisent par un déplacement arbitraire dans le plan 2D, ce déplacement est un vecteur 2D dans n'importe quelle direction et de n'importe quelle longueur. De ce vecteur, vous pouvez extraire l'axe y et l'axe x.
Que faire si le vecteur de déplacement est trop grand?
Vous ne voulez pas ça:
Comme nos nouveaux mouvements appliquant la logique traitent d'abord un axe puis l'autre, un grand déplacement peut finir par être vu par le code de collision comme un grand L, cela ne détectera pas correctement les collisions avec des objets qui coupent votre vecteur de déplacement.
La solution consiste à diviser les déplacements importants en petites étapes, idéalement votre largeur ou hauteur de sprite, ou la moitié de votre largeur ou hauteur de sprite. Pour obtenir quelque chose comme ça:
Qu'en est-il de la téléportation des sprites?
Si vous représentez la téléportation comme un grand déplacement, en utilisant le même composant de mouvements qu'un mouvement normal, alors pas de problème. Si vous ne le faites pas, alors vous devez penser à un moyen de marquer le sprite de téléportation à traiter par le code de collision (en ajoutant à une liste dédiée à cet effet?), Pendant le code de réponse, vous pouvez directement déplacer le sprite debout qui était dans la façon dont la téléportation a eu lieu.
2 - Phantom AABB
Avoir un AABB secondaire pour chaque sprite affecté par la gravité qui commence au bas du sprite, a la même largeur de l'AABB de sprite et une hauteur de 1 pixel.
L'idée ici est que ce fantôme AABB entre toujours en collision avec la plate-forme sous le sprite. Ce n'est que lorsque cet AABB fantôme ne chevauche pas que la gravité peut être appliquée au sprite.
Vous n'avez pas besoin de calculer le vecteur de pénétration pour l'AABB fantôme et la plate-forme. Vous n'êtes intéressé que par un simple test de collision, s'il chevauche une plate-forme, la gravité ne peut pas être appliquée. Votre sprite peut avoir une propriété booléenne qui indique s'il se trouve sur une plate-forme ou dans les airs. Vous êtes dans les airs lorsque votre AABB fantôme n'entre pas en collision avec une plate-forme en dessous du joueur.
Cette:
Devient quelque chose comme ça:
Très simple et fiable.
Vous laissez votre implémentation AABB telle quelle.
Le fantôme AABB peut avoir une boucle dédiée qui les traite, qui ne vérifie que les collisions simples et ne perd pas de temps à calculer le vecteur de pénétration. Cette boucle doit être antérieure au code de collision et de réponse normal. Vous pouvez appliquer la gravité pendant cette boucle, car les sprites dans l'air appliquent la gravité. Si l'AABB fantôme est en soi une classe ou une structure, il peut avoir une référence au sprite auquel il appartient.
Ceci est très similaire à l'autre solution proposée où vous vérifiez si votre joueur saute. Je donne un moyen possible de détecter si le joueur a les pieds sur une plate-forme.
la source
Je combinerais les tuiles de plate-forme en une seule entité de plate-forme.
Par exemple, supposons que vous preniez les 3 tuiles des images et les combiniez en une seule entité, votre entité aurait une boîte de collision de:
L'utiliser pour la collision vous donnera le y comme axe le moins pénétrant sur la majeure partie de la plateforme tout en vous permettant d'avoir des tuiles spécifiques dans la plateforme.
la source
De tels problèmes sont courants avec les nouvelles méthodes de détection des collisions.
Je ne sais pas trop sur votre configuration de collision actuelle, mais voici comment cela devrait se passer:
Tout d'abord, faites ces variables:
Ensuite, créez une minuterie pour calculer le delta afin d'affecter la vitesse de l'objet.
Troisièmement, procédez comme suit:
Si vous faites cela, il ne devrait y avoir aucun problème. J'espère que cela t'aides!
la source