Comment empêcher le personnage de mon plateforme de s'accrocher aux carreaux muraux?

9

Actuellement, j'ai un jeu de plateforme avec des tuiles pour le terrain (graphiques empruntés à Cave Story). Le jeu est écrit à partir de zéro en utilisant XNA, donc je n'utilise pas un moteur existant ou un moteur physique.

Les collisions de carreaux sont décrites à peu près exactement comme décrit dans cette réponse (avec SAT simple pour les rectangles et les cercles), et tout fonctionne bien.

Sauf lorsque le joueur rencontre un mur en tombant / sautant. Dans ce cas, ils attraperont une tuile et commenceront à penser qu'ils ont touché un plancher ou un plafond qui n'est pas réellement là.

Image de capture de personnage

Dans cette capture d'écran, le joueur se déplace vers la droite et tombe vers le bas. Ainsi, après le mouvement, les collisions sont vérifiées - et d'abord, il s'avère que le personnage du joueur entre en collision avec la tuile 3e du sol et est poussé vers le haut. Deuxièmement, il s'est avéré entrer en collision avec la tuile à côté de lui et poussé sur le côté - le résultat final étant que le personnage du joueur pense qu'il est au sol et ne tombe pas, et `` attrape '' la tuile aussi longtemps qu'il s'y heurte. .

Je pourrais résoudre ce problème en définissant les carreaux de haut en bas à la place, ce qui le fait tomber en douceur, mais le cas inverse se produit et il heurte un plafond qui n'est pas là en sautant vers le haut contre le mur.

Comment dois-je aborder la résolution de ce problème, afin que le personnage du joueur puisse simplement tomber le long du mur comme il se doit?

doppelgreener
la source
Similaire à cette question? gamedev.stackexchange.com/questions/14486/…
MichaelHouse
@ Byte56 La question n'est pas la même, mais sa réponse pourrait aider.
doppelgreener
Une note: J'ai eu très peu de temps au cours des deux dernières semaines pour expérimenter les réponses ici. J'ai implémenté la résolution des collisions dans le monde comme indiqué dans la question liée à Byte56, qui a des bogues séparés à des vitesses élevées, et je n'ai pas encore pu essayer les réponses à cette question. J'essaierai ceux-ci quand je le pourrai et j'accepterai une réponse quand je le pourrai, ou publierai la mienne si je la résous moi-même.
doppelgreener

Réponses:

8

L'approche la plus simple et la plus résistante aux pannes consiste simplement à ne pas vérifier les collisions sur les bords cachés. Si vous avez deux tuiles murales, l'une directement au-dessus de l'autre, le bord inférieur de la tuile supérieure et le bord supérieur de la tuile inférieure ne doivent pas être vérifiés pour la collision contre le joueur.

Vous pouvez déterminer les bords valides lors d'un simple passage sur la carte des tuiles, en stockant les bords extérieurs sous forme de drapeaux sur chaque emplacement de tuile. Vous pouvez également le faire au moment de l'exécution en vérifiant les tuiles adjacentes lors de la détection de collision.

Il existe des versions plus avancées de cette même technique qui facilitent et accélèrent également la prise en charge des tuiles non carrées.

Je vous recommande de lire les articles ci-dessous. Ce sont de simples introductions décentes à la physique de base et à la détection de collisions dans les plateformes modernes (comme Cave Story). Si vous optez pour une ambiance Mario plus old-school, il y a quelques astuces supplémentaires à vous soucier, mais la plupart d'entre elles impliquent de gérer les sauts et les animations plutôt que les collisions.

http://www.metanetsoftware.com/technique/tutorialA.html http://www.metanetsoftware.com/technique/tutorialB.html

Sean Middleditch
la source
Pouvez-vous préciser comment ignorer certains bords lors d'une collision? Je vois généralement les collisions détectées comme l'intersection de deux rectangles (les limites du joueur et les limites de la tuile). Lorsque vous dites de ne pas vérifier la collision par rapport aux bords individuels, cela signifie-t-il que l'algorithme de collision n'est pas une intersection rectangle-rectangle mais autre chose? Votre description est logique, mais je ne sais pas à quoi ressemblerait l'implémentation.
David Gouveia
Ouais, fais juste une collision rectangle. Simplifié car la ligne est toujours alignée sur l'axe. Vous pouvez simplement décomposer votre case à cocher.
Sean Middleditch
5

Voici l'ordre je ferais des choses ....

1) Enregistrer le X, Y du caractère actuel

2) Déplacez la direction X

3) Vérifiez tous les coins du caractère en obtenant les données de tuile et vérifiez si elles sont solides en procédant comme suit: (X et Y est la position du caractère)

  • en haut à gauche: sol (X / tileWidth), sol (Y / tileHeight);
  • en haut à droite: étage ((X + characterWidth) / tileWidth), étage (Y / tileHeight);
  • en bas à gauche: sol (X + / tileWidth), sol ((Y + characterHeight) / tileHeight);
  • en bas à droite: étage ((X + characterWidth) / tileWidth), étage ((Y + characterHeight) / tileHeight);

... pour obtenir des données de mosaïque correctes à partir des données cartographiques

4) Si l'une des tuiles est solide, vous devez forcer X à la meilleure position possible en restaurant le X enregistré et ...

  • si vous vous déplacez à gauche: X = étage (X / tileWidth) * tileWidth;
  • si vous vous déplacez à droite: X = ((floor ((X + characterWidth) / tileWidth) + 1) * tileWidth) - characterWidth;

5) Répétez 2-4 en utilisant Y

Si votre personnage est plus grand qu'une tuile, vous devez vérifier plus de tuiles. Pour ce faire, vous devez boucler et ajouter tileHeight à la position du caractère Y pour obtenir la tuile

int characterBlockHeight = 3;

// check all tiles and intermediate tiles down the character body
for (int y = 0; y < characterBlockHeight - 1; y++) {
    tile = getTile(floor(characterX / tileWidth), floor((characterY + (y * tileHeight)) / tileHeight));
    ... check tile OK....
}

// finally check bottom
tile = getTile(floor(characterX / tileWidth), floor((characterY + characterHeight) / tileHeight);
... check tile OK....

Si le caractère est plus large que 1 bloc, faites de même mais remplacez characterY, characterHeight, tileHeight, etc. par characterX, characterWidth, etc.

John
la source
2

Que se passe-t-il si vous avez vérifié la possibilité d'une collision dans le cadre de la méthode de mouvement du joueur et avez rejeté le mouvement qui a fait glisser le joueur dans un autre objet? Cela empêcherait le joueur de pénétrer «à l'intérieur» d'un bloc, et donc il ne pourrait pas être «au-dessus» du bloc en dessous.

Un gars
la source
1

J'ai rencontré presque exactement le même problème dans un jeu sur lequel je travaille actuellement. La façon dont je l'ai résolu était de stocker la zone de la partie qui se chevauchait lors du test des collisions, puis de gérer ces collisions en fonction de leur priorité (la plus grande zone en premier), puis de vérifier chaque collision pour voir si elle avait déjà été résolue par un précédent. manipulation.

Dans votre exemple, la zone de la collision de l'axe des x serait supérieure à l'axe des y, elle serait donc gérée en premier. Ensuite, avant de tenter de gérer la collision sur l'axe des y, il vérifierait et verrait qu'elle ne se heurte plus après avoir été déplacée sur l'axe des x. :)

Nick Badal
la source
1

Une solution simple, mais pas parfaite, consiste à changer la forme de la boîte de collision du joueur afin qu'elle ne soit pas un carré. Coupez les coins pour le rendre octogonal ou quelque chose de similaire. De cette façon, quand il entre en collision avec une tuile comme celles du mur, il en glisse et ne se fait pas prendre. Ce n'est pas parfait parce que vous pouvez toujours le remarquer attraper un peu dans les coins, mais il ne restera pas coincé et c'est très facile à mettre en œuvre.

Amplify91
la source
1

Un problème très similaire a été abordé dans ce didacticiel: Introduction au développement de jeux . La partie pertinente de la vidéo est à 1:44 , expliquant avec des diagrammes et des extraits de code.

L'avis 14-tilemap.py présente le problème d'écrêtage, mais il est corrigé dans 15-blocker-sides.py

Je ne peux pas expliquer exactement pourquoi le dernier exemple de didacticiel ne coupe pas alors que votre code le fait, mais je pense qu'il a quelque chose à voir avec:

  • Réponse de collision conditionnelle basée sur le type de tuile (dont les bords sont réellement actifs.)
  • Le joueur «tombe» toujours. Bien que le joueur semble reposer sur une tuile, le joueur tombe en fait à plusieurs reprises à travers la tuile, puis est repoussé vers le haut. (Le self.restingmembre dans le code est uniquement utilisé pour vérifier si le joueur peut sauter. Malgré cela, je ne peux pas obliger le joueur à faire un "double" saut depuis un mur. En théorie, cela pourrait être possible, cependant)
Leftium
la source
0

Une autre méthode (à laquelle j'ai répondu à la question liée à Byte56) serait de vérifier si la résolution de collision met le personnage dans un endroit vide ou non. Donc, dans votre problème, vous obtenez une collision du plafond du rectangle intérieur pour le déplacer vers le haut, ce qui serait illégal (car vous êtes toujours en collision avec une autre tuile). Au lieu de cela, vous ne le déplacez que si vous êtes déplacé dans un espace libre (comme la façon dont la collision de la tuile supérieure vous déplacerait vers la gauche), et une fois que vous avez trouvé cette collision, vous avez terminé.

La réponse que j'ai donnée avait du code, mais c'est devenu trop compliqué. Je garderais une trace de toutes les collisions dans ce délai et ne prendrais que celle qui mène à un espace libre. Cependant, s'il n'y en avait pas, je pense que j'ai recouru à réinitialiser la position du personnage à sa dernière position, mais c'est vraiment moche et peut-être que vous préféreriez essayer d'implémenter quelque chose comme le Mario d'origine où il se déplace dans une direction ou quelque chose quand il n'y a pas de résolution d'espace libre est possible. Vous pouvez également trier cette liste de résolutions de collision et passer uniquement à l'espace libre avec la distance la plus courte (je pense que cette solution serait la plus préférable, même si je n'ai pas codé pour cela).

Jeff
la source
0

Je n'ai fait que 3 SAT en 3D, alors peut-être que je ne comprends pas bien. Si je comprends votre problème, cela pourrait être dû à des contacts avec un axe de contact qui ne devraient pas être possibles, entraînant le joueur à agir comme s'il y avait un plancher. Une solution de contournement pourrait être d'utiliser à la place une forme de cercle pour votre personnage, alors vous n'obtiendrez des axes de contact que dans la direction de l'axe x lorsque vous frappez un mur.

Mikael Högström
la source