Sujets-libres.fr

Informatique, logiciels libres, internet, humeurs et… le reste !

Gestion des collisions dans un jeu 2D / vue de dessus

Rédigé par -Fred- Aucun commentaire
Introduction :

Il y a peu, j'ai lu à propos de mon système de gestion des collisions qu'il semblait compliqué et qu'il serait judicieux de le revoir. N'en ayant alors pas réellement fait la description, j'ai donc voulu le détailler.

Lorsque j'ai débuté la création de mon clone de Zelda, il a fallu à un moment que je développe un système de gestion des collisions entre les éléments se déplaçant sur la carte et les éléments constituant cette même carte. Ce système ayant pour contrainte d'être le plus fidèle possible par rapport au jeu que clone. A priori, lorsqu'on ne s'est jamais penché sur le sujet, rien de bien compliqué. Pas si sûr en fait car une fois que l'on commence à y réfléchir et à le développer, de multiples problèmes apparaissent. Voilà pourquoi j'ai voulu partager ma modeste expérience sur ce sujet en expliquant comment j'ai procédé pour arriver à un résultat fonctionnel (histoire aussi que le temps que j'y ai passé et perdu (parfois) puisse profiter à d'autres).

Qui est concerné par le système de gestion des collisions :


  • En premier lieu, c'est bien entendu les éléments de décors. Chaque tuile du décors est totalement, partiellement ou pas du tout traversable. J'en reparle ensuite.

  • Le personnage que contrôle le joueur via le clavier.

  • Les personnages non joueur (appelés par la suite PNJ).


Ce sont les PNJ et le personnage que l'on contrôle qui appellent le système de gestion des collisions.

Rôle du système de gestion des collisions :


  • Bloquer ou non le ou les déplacements demandés le personnage ou les PNJ. Les personnages se déplacent sur l'axe X et sur l'axe Y.

  • Corriger si besoin les déplacements du personnage et des PNJ (quoi de plus énervant en effet que d'être bloqué alors que l'on est seulement décalé de quelques malheureux pixels).

  • Si deux déplacements orthogonaux sont demandés (typiquement, aller à la fois vers le haut et vers la gauche), que l'on peut aller dans l'une ou l'autre des direction mais que l'on ne peut pas aller dans l'une et l'autre des directions (se déplacer en diagonale quoi), le système de gestion des collisions doit prioriser le dernier déplacement demandé.



Spécificités propres au projet que je développe :


  • Chaque tuile est un carré de 16 pixels de côté.

  • La partie sensible aux collisions de chaque personnage est lui aussi un carré de 16 pixels de côté. Ce choix est discutable mais il simplifie en partie le problème pour la suite.

  • Les personnages peuvent se déplacer par pas de 1px, 2px ou de 4px (c'est selon). Ils ne se trouvent pas nécessairement en face d'une seule tuile.

  • Pour information, bien que ce ne soit pas directement implémenté par le système de gestion des collisions, la propriété traversante de chaque tuile du tileset est définie une fois pour toutes en dur dans le programme. Cette propriété peut prendre une valeur allant de 0 à 15 et traduit 16 possibilités. En effet, chaque tuile est découpée en 4 et chaque partie a un poids propre (1, 2, 4 et 8). Une tuile à 0 est totalement traversable.

  • La position du personnage à l'écran dépend de sa position sur la carte et de la taille de la carte. Cela signifie que tant que le personnage contrôlé par le joueur est au centre d'une grande carte, il reste au centre de l'écran et que s'il se déplace, c'est la carte qui bouge sous lui dans le sens inverse au déplacement demandé.




Informations nécessaires avant d'être en mesure de tester une collision lors d'un déplacement :


  • Tout d'abord, les coordonnées du personnage. Dans mon cas, je prend toujours le même pixel en haut à gauche.

  • Comme mes personnages ont tous une zone sensible au collisions de la même taille que mes tuiles (16px par 16px), chaque côté de cette zone sensible ne peut se trouver que face à deux tuiles (voir une seule). Comme je l'ai écrit plus haut, ça simplifie un peu le problème.

  • En fonction de chaque déplacement demandé, j'utilise les coordonnées du personnage et sa vitesse de déplacement dans cette direction pour définir quelles sont les deux tuiles à tester. Plus la vitesse de déplacement est élevée, plus on teste une tuile éloignée.



Implémentation de la fonction de gestion des collisions :
void gestionCollisions(int decalageFenetreX,int decalageFenetreY,position **table,bool testVertical, int vitessePersoSurX, int vitessePersoSurY);


Séquencement du système de gestion des collisions :

  • Définition des variables locales à utiliser.

    int typeBlocTraversable0,typeBlocTraversable1;
    Variables indiquant le type traversable des deux tuiles à tester

    int blocTraversable0_X = 0,blocTraversable0_Y = 0,blocTraversable1_X = 0,blocTraversable1_Y = 0;
    Coordonnées des tuiles à tester

    bool bloc0Traversable = true,bloc1Traversable = true;
    Indique l'état traversable ou non de la partie de chaque tuile testée

    int pX0,pY0;
    Coordonnée du personnage. Ces coordonnées sont directement renseignées ensuite.


  • Comme indiqué plus haut, il faut prioriser le dernier déplacement demandé si les deux derniers déplacements sont orthogonaux. Voilà pourquoi on a besoin du bool testVertical. La fonction ne gère donc qu'un seul axe à la fois. Cette fonction de gestion des collisions est donc d'abord appelée pour gérer les déplacements sur un axe puis ultérieurement pour l'autre axe (j'y reviendrai).

  • Selon la direction de déplacement demandée par le personnage, il faut maintenant déterminer le type de bloc traversable de chacune des tuiles que l'on veut traverser (rappelez-vous, les valeurs comprises entre 0 et 15). Les coordonnées du personnage et sa vitesse de déplacement dans la direction concernée permettent de sortir les bonnes tuiles à tester.

  • Si l'une ou l'autre des tuiles à traverser n'est pas entièrement traversable, il faut passer quelques tests.

  • Le but des tests est de déterminer s'il y un obstacle sur le chemin immédiat du personnage. Il faut donc déterminer finement les zones de chaque tuile qu'il faut tester.

    • Le premier test consiste à dire si oui ou non on veut tester la seconde moitié des tuiles ("seconde" en fonction du sens de déplacement du personnage).
    • Second test, déterminer laquelle des deux tuiles à tester va être la plus recouverte par le personnage.
    • Ensuite, c'est très simple : pour chaque tuile à tester et en fonction de son typeBlocTraversable, on sait dire si pour le personnage, la tuile est traversable ou non.

    • Voilà grossièrement ce que cela donne. Les numéros indiquent le "poids" de chaque partie de tuile. Dessous, c'est la représentation du personnage et en bleu sa partie sensible aux collisions. A droite, deux cas possibles dans le cas d'un déplacement demandé à gauche. Les lignes jaune symbolisent la vitesse déplacement du personnage et le point rouge la zone à tester.
      Gestion collisions
      Dans les deux cas, deux tuiles sont à tester, la grise et la noire. Chaque tuile est vue traversable si la sous partie que l'on vient tester l'est elle même.

    • Si l'une ou l'autre des tuiles n'est pas traversable sur sa partie testée, le déplacement est stoppé.

    • Il serait possible de s'arrêter là mais les personnages et surtout le joueur risqueraient souvent d'être bloqués car pas calés au pixel près à certains endroits. Il faut donc implémenter un système de correction des déplacements. Très simple à mettre en place : si sur les deux blocs testés un est traversant et l'autre non, on redirige le personnage vers ce bloc traversant. Si vous avez bien suivi, ce déplacement est forcement orthogonal au déplacement initial bloqué.






Intégration de la fonction de gestion des collisions aux déplacements des personnages :
Plusieurs étapes se succèdent :

  • la demande initiale de déplacement (fonction du clavier ou d'une IA).

  • les corrections à apporter au dernier déplacement demandé (car pour rappel, on peut demander un déplacement sur X et un autre sur Y en même temps).

  • l'application du déplacement sur les coordonnées du personnage.

  • les corrections à apporter à l'avant dernier déplacement (s'il est toujours actif)

  • l'application du déplacement sur les coordonnées du personnage.

  • l'affichage du personnage sur sa nouvelle position



Conclusion :
Encore un fois, ça n'a l'air de rien, à priori. Pourtant, au fur et à mesure que j'ai avancé sur le sujet j'ai été confronté à des problèmes ou des bugs que je n'avais pas imaginé du tout. Alors oui, à mon humble avis, cette partie (de mon code) peut sembler complexe au premier abord mais elle fonctionne. Je suis preneur de toute solution plus simple et qui fonctionne aussi bien. J'espère toutefois que ça pourra aider au besoin.

............

Sources :
Pour le moment, le jeu est toujours en cours de développement et le code source est toujours susceptible d'être modifié. Je ne le met donc pas de suite à disposition. Cela sera fait lorsque je distribuerai un jeu fonctionnel. Si pour une raison ou une autre vous vouliez avoir quand même accès au sources, pas de problème mais il faudra m'en faire la demande par mail (blog arobase sujets tiret libres point fr).

Les commentaires sont fermés.

Fil RSS des commentaires de cet article