TDM n°9 – Gérer les collisions !
Posté le 21/11/2018 18:00
Le Tuto Du Mercredi [TDM] est une idée qui fut proposée par Ne0tux. Un mercredi sur deux, nous postons un tutoriel sur l'Utilisation de la calculatrice, le Transfert, les Graphismes, la Programmation, ou encore la Conception de jeu. Ce TDM-ci en explique un point important et des fois compliqué ! Mention spéciale à Ninestars qui a rédigé le contenu de cette édition.
TDM n°9 – Comment gérer les collisions d'un personnage ?
Niveau : ★ ★ ★ ★ ☆
Tags : Basic Casio, Jeux, Collision, Personnage
Comment faire un système qui permet de gérer les collisions d'un personnage avec le décor, ou avec tout autre chose ? Nous allons détailler plusieurs méthodes possibles dans ce TDM.
Hypothèses : Nous nous plaçons dans le cas d'un jeu vu de haut, avec un personnage qui peut se déplacer selon deux axes. On suppose également que la carte dans laquelle se déplace le joueur est composée de
tiles – tuiles en français. On parle souvent de
Zelda-like.
Le personnage que déplace le joueur est positionné grâce à deux variables :
U et V, respectivement l'abscisse et l'ordonnée. On suppose que le coin inférieur bas est l'origine du repère, c'est-à-dire que U et V valent 0 à cet endroit.
Plan d'action :
Dans la boucle principale du jeu, il faut réaliser ces actions dans un ordre précis :
1) L'action du joueur sur son clavier va modifier la valeur de ces variables
2) On vérifie si le joueur sort de la carte
3) On vérifie si la position souhaitée est bloquée
4) On vérifie si la position souhaitée déclenche une action
5) Si oui, déplacer le joueur ou déclencher l'action
Partie I – Que veut faire le joueur ?
La meilleure méthode pour gérer l'action du joueur est d'utiliser des variables intermédiaires enregistrant la position souhaitée ; nous utiliserons donc
I et
J. Ensuite il y aura un tas de conditions à remplir, et si tout va bien, la position souhaitée deviendra la position du joueur.
Début de l'exemple :
5->U
3->V
...
Do // début de boucle
U->I
V->J //On initialise I et J
While 1 // Sous-boucle, qui nous sert de raccourci avec la commande Break
Do
GetKey->G
LpWhile Not G //la boucle tourne tant que le joueur n'actionne pas de commande
G=37⇒Dsz J //↓
G=38⇒Dsz I //←
G=27⇒Isz I //→
G=28⇒Isz J //↑
If I≠U Or J≠V //Si le joueur a appuyé sur une touche directionnelle
Then
// la suite en partie II
IfEnd
I->U
J->V
WhileEnd
LpWhile 1 // fin de boucle
I et J donnent la position désirée par le joueur. En plus on peut savoir si le joueur a souhaité se déplacer en vérifiant que
I≠U ou J≠V. Il est plutôt important d'utiliser cette condition pour éviter de vérifier les collisions, donc avoir un jeu plus fluide, et éviter le clignotement de l'écran. En effet pas besoin de redessiner l'écran si rien ne change !
Partie II – Le joueur sort-il de la carte ?
Pour cela, il suffit de vérifier si la position souhaitée est en dehors de la carte. Soit une carte de dimensions
W (width ; la largeur) et
H (height ; la hauteur) :
If I<0 Or I>W Or J<0 Or J>H // Si le personnage sort de la carte
Then
// Alors on apporte les modifications nécessaires...
Break //On sort de la sous-boucle
Else
// Sinon, le personnage reste dans la carte
// la suite en partie III
IfEnd
En fonction du jeu, soit on bloque le joueur, soit on change la carte.
Pour bloquer le joueur, il suffit de sauter avec un
Break qui nous amène à la fin du code (juste après le
WhileEnd) : le changement des coordonnées du personnage est donc sauté. Il reste alors à sa place.
Partie III – La position souhaitée est-elle atteignable ?
À partir de maintenant, tout va dépendre de la façon dont l'information de la carte est enregistrée.
Il existe plusieurs méthodes, certaines très efficaces, d'autre moins.
Première méthode : La matriceC'est la méthode triviale ; il suffit d'enregistrer de façon naturelle la position des objets sur la carte, dans une matrice de la taille de la carte. Dans cet exemple, la matrice
Mat A fait 16×8 cases et par défaut est remplie de 0.
Les autres nombres correspondent à des objets sur la carte : ce peut être un personnage, une rivière, un arbre, un mur, une maison, un coffre... La valeur indique la nature de l'objet.
Ici il suffit de vérifier que la valeur est 0 :
If Mat A[I,J]=0 //s'écrit aussi If Not Mat A[I,J
Then
// dans ce cas rien ne bloque le joueur
// la suite partie V
IfEnd
Partie IV – Déclencher une action ?
Juste à la suite, on peut ajouter des conditions sur la nature de l'objet, et exécuter du code à ce moment là.
If Mat A[I,J]=3
Then // action
IfEnd
Il peut y avoir des subtilités, par exemple si un objet est présent (donc la case de la matrice est différente de 0) mais que le joueur peut passer au travers. Dans ce cas, il suffit de gérer l'exception :
If Mat A[I,J]=7
Then
// le joueur peut passer au travers
// la suite partie V
// action
IfEnd
Une méthode autre serait d'identifier les éléments que le joueur peut traverser (herbes, escaliers, etc.) par une valeur négative et ceux qu'il ne peut pas traverser (murs, arbres, rivières, etc.) par une valeur positive, plutôt que de gérer plein d'exceptions :
If 0<Mat[I,J //Si la valeur est strictement positive
Then Break //Le joueur est bloqué, on sort de la sous-boucle
Else //Sinon, c'est que la valeur est nulle ou négative : on peut passer !
//On gère ici les exceptions, les éventuelles actions en fonction de l'objet traversé
IfEnd
Partie V – Déplacer le joueur :
Dans tous les cas, si le joueur peut aller où c'est possible, il suffit de faire
I->U:J->V pour que la position souhaitée du joueur deviennent la position réelle.
I->U
J->V
Code final avec indentation :
5->U //début de programme
3->V
...
Do // Boucle principale
U->I
V->J
While 1 // Sous-boucle, qui nous sert de raccourci
Do
GetKey->G
LpWhile Not G
G=37⇒Dsz J //↓
G=38⇒Dsz I //←
G=27⇒Isz I //→
G=28⇒Isz J //↑
If I≠U Or J≠V //Si le joueur a appuyé sur une touche directionnelle
Then
If I<0 Or I>W Or J<0 Or J>H // Si le personnage sort de la carte
Then
// Alors on apporte les modifications nécessaires...
// Par exemple, on change I et J et on entre dans un sous-programme pour gérer cela.
Break //On sort de la boucle
Else
// Sinon, le personnage reste dans la carte
If 0<Mat[I,J] //Si on ne peut pas passer
Then
Break //On sort de la sous-boucle ; U et V ne sont pas modifiées
Else
If Mat[I,J]<0 //Si la valeur est négative
Then //On gère les éventuelles exceptions
IfEnd
IfEnd
IfEnd
IfEnd //La fin de nos quatre If
I->U
J->V
WhileEnd
LpWhile 1
Code final sans commentaire :
5->U
3->V
...
Do
U->I
V->J
While 1
Do
GetKey->G
LpWhile Not G
G=37⇒Dsz J
G=38⇒Dsz I
G=27⇒Isz I
G=28⇒Isz J
If I≠U Or J≠V
Then
If I<0 Or I>W Or J<0 Or J>H
Then ...
Break
Else
If 0<Mat[I,J
Then Break
Else If 0>Mat[I,J
Then ...
IfEnd:IfEnd:IfEnd:IfEnd
I->U
J->V
WhileEnd
LpWhile 1
Méthodes alternatives
Ces méthodes ne sont pas forcement plus rapides, ou plus simples. D'ailleurs certaines ne sont pas adaptées au Basic.
Méthode par recherche :
La méthode des matrices est simple, rapide, mais la carte est très granuleuse et sa taille est vite limitée par une consommation de mémoire excessive. En effet, une grande carte sera en majorité remplie de 0, c'est du gâchis.
La méthode par recherche consiste à enregistrer dans une Matrice
Mat en Basic, ou un tableau de structures en C, l'ensemble des objets présents sur la carte, ainsi que leurs coordonnées. Puis quand le joueur souhaite se déplacer, rechercher dans l'ensemble de ce tableau si un objet à les mêmes coordonnées que la position souhaitée.
0->F
For 0->K To Nombre d'objets
If I=Mat A[K,1] And J=Mat A[K,2]
Then
// collision avec l'objet ID Mat A[K,3]
1->F
// passer à la partie IV
IfEnd
Next
If F=0
Then I->U:J->V
IfEnd
F est un flag servant à savoir s'il y a eu une collision avec au moins un objet. Si F=0 alors il n'y a pas eu de collision et on déplace le joueur.
De cette manière, il est même possible de définir la largeur et la hauteur des objets, il suffit de modifier la condition pour que I et J ne soit plus strictement égaux, mais compris dans les intervalles.
Cette méthode est assez peu adaptée au Basic vu quelle nécessite plus calcul. Néanmoins le calcul listique peut accélérer la recherche. En C, cette méthode est très efficace, et les performances de la calculatrice sont (vraiment) largement suffisantes, même pour 300 objets.
Méthode par équation :
Cette méthode est plutôt utilisée pour des collisions d'objets avec des formes complexes, et pas forcement alignées sur le repère.
Supposons qu'un mur soit défini par deux points A et B. Des calculs permettent de savoir si le personnage de coordonnées (U;V) est à gauche ou à droite de la droite. Si il passe de gauche à droite, ou de droite à gauche, c'est que le joueur croise le mur, donc il y a collision.
Ce test peut être réalisé avec l'équation paramétrique de la droite, ou avec le signe du produit scalaire. Ces segments peuvent être mis bout-à-bout pour former une forme complexe, ouverte comme fermée. Plus d'informations sur les technique de calcul de collision en fin de page parmi les liens utiles.
C'est ainsi que se finit le neuvième TDM, dense et assez spécial puisque
Ninestars en a rédigé le contenu ! Je tenais à m'excuser auprès de lui pour les quelques légères modifications que j'ai apporté à son code : je préfère ne pas avoir de
Lbl !
Liens utiles :
En apprendre davantage sur les différentes
méthodes de collisions sur le Site du Zéro !
(edit: lien mort)
Consulter l'ensemble
des TDM disponibles.
Soumettre
des suggestions de TDM sur cette page !
Citer : Posté le 23/11/2018 21:26 | #
Je viens de voir, difficulté 4/5 c'est pas un peu trop ?
Citer : Posté le 23/11/2018 22:23 | #
Si tu considères 5/5 comme étant le niveau d'un programmeur Basic honnête, ça passe. Comme le TDM est plutôt du genre accessible, je trouve ce niveau de difficulté mérité. Si ç'avait était le Rendez-Vous des Experts (RVE), ç'aurait été mesuré différement.
Citer : Posté le 24/11/2018 13:56 | #
Oui Lephé, je plussoie. J'ai considéré le contenu assez dense et complexe, donc j'ai mis 4 étoiles.
Citer : Posté le 24/11/2018 14:18 | #
D'accord, je ne l'avais pas compris comme cela désolé
Citer : Posté le 24/11/2018 14:30 | #
Y'a pas à être désolé ! On va pas t'engueuler parce que t'as posé une question non plus
Citer : Posté le 19/12/2018 08:29 | #
J'ai fait un programme de map avec colisions sur le graph 90. Il y a quelques obstacles (lacs, forets, montagnes et villages). Je me demandais si ça vous intéressait ?
-Planétarium 2
Citer : Posté le 19/12/2018 10:29 | #
On peux peut-être t'aider à le faire (je reçois la mienne aujourd'hui Met-le De plus ça peut faire un bel exemple
Citer : Posté le 19/12/2018 10:36 | #
Excuse je me suis mal exprimé, je n'ait pas de problèmes pour le faire, je demandais juste si ça serait interessant comme exemple pour le TDM.
-Planétarium 2
Citer : Posté le 19/12/2018 18:13 | # | Fichier joint
Bon, avec un peu de retards, je poste ici un exemple de la méthode avec matrice fonctionnant en mode locate. Le programme est sous graph90 (en g3m), donc si quelqu'un le veut en g2m, qu'il me le dise.
Voici un aperçu du code:
ClrText
ClrMat
'Initialisation de la map'
[[0,0,0,1,1,1,1,0,0,0,0,0,4,4,4,0,0,0,2,2,2][0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,2,2][0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0][0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,0][2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1][2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1]]->Mat A
'Affichage de la map'
For 1-><r> To 21
For 1->Theta To 6
Mat A[Theta,<r>]=1=>Blue Locate <r>,Theta,"_#E6B1_"
Mat A[Theta,<r>]=2=>Green Locate <r>,Theta,"_#E5E1_"
Mat A[Theta,<r>]=3=>Yellow Locate <r>,Theta,"_#E6A6_"
Mat A[Theta,<r>]=4=>Cyan Locate <r>,Theta,"_#E69C_"
Next
Next
1->A~B
While Getkey<>47
'Affichage du personnage'
Black Locate A,B,"_#E59A_"
Do
LpWhile Getkey=0
'Commandes de deplacements X'
Locate A,B," "
If A<21 And Getkey=27
Then Mat A[B,A+1]=0=>Isz A
IfEnd
If A>1 And Getkey=38
Then Mat A[B,A-1]=0=>Dsz A
IfEnd
'Commandes de deplacements Y'
If B<6 And Getkey=37
Then Mat A[B+1,A]=0=>Isz B
IfEnd
If B>1 And Getkey=28
Then Mat A[B-1,A]=0=>Dsz B
IfEnd
WhileEnd
Le programme est en lien sans les annotations
-Planétarium 2
Citer : Posté le 19/12/2018 19:13 | #
C'est exactement ça, ça doit bien fonctionner du coup
Citer : Posté le 19/12/2018 19:22 | #
Ben oui, parfaitement, en plus c super fluide Mais tu sais, je ne l'ait pas fait avec l'aide du tuto de Drack (aussi bien soit-t'il), j'avais déjà compris comment il fallait procéder avant même que le tuto paraisse (je ne me vante pas du tout là )
Mais du coup je voulais mettre une image du rendu mais je sais pas comment faire vu que je ne l'ait pas mise sur internet, elle est en jpg..
-Planétarium 2
Citer : Posté le 01/09/2019 16:15 | #
Le lien vers OpenClassroom en fin d'article est mort
Citer : Posté le 01/09/2019 16:36 | #
Malheureusement l'article ne semble plus exister et je ne l'ai pas trouvé sur l'archive...
Citer : Posté le 09/05/2023 09:32 | #
Pour le désaventage de la méthode "de recherche" ça peut être contré, tu mets simplement rien au lieu d'un zéro, et tu regarde si c'est
Caltos : G35+EII, G90+E (briquée )
Citer : Posté le 09/05/2023 13:49 | #
L'histoire de la méthode par recherche c'est justement qu'en ne matérialisant pas toutes les cases de la map dans la matrice, on économise tous les zéros. La liste d'objets élimine entièrement la représentation de ces objets nuls. (Par contre le temps de recherche peut être long, et donc il faut accélérer s'il y a beaucoup d'objets, quadtrees, BSP, etc).
Ironiquement un pointeur nul prend quand même 4 octets donc souvent on essaie de mettre des petits entiers à la place
Citer : Posté le 09/05/2023 13:55 | #
Ah je croyais que ça prenait zéro quoi, c'est vrai qu'en y pensant c'est pas vraiment possible. Mais c'est vrai que si t'as que quelques tiles différentes (différents ?) ça doit être plus efficace d'avoir des petits entiers
Caltos : G35+EII, G90+E (briquée )