Tutoriel
Les collisions en gml
Collisions avancées
Je vais vous apprendre dans ce tutoriel (du moins je l’espère) à utiliser correctement les collisions en gml.
Avant d’énumérer ce dont je vais vous parler, je vais vous dire de quoi je ne parlerai pas.
Je ne parlerais pas des mask, vu que ça ne change rien par rapport à un sprite. Je ne parlerais pas de lengthdir_x/y, révisez votre trigonométrie. Je ne parlerais strictement que des collisions, moins les collisions géométriques.
Allez, savoir pourquoi, beaucoup de gens critique le moteur de collision de game maker, pour des raisons qui pourraient être évitées si ils savaient vraiment comment ça fonctionne.
Je commencerai par préciser le fonctionnement du moteur de collision de Game Maker, puis je vais détailler les fonctions de long en large, puis donner des résolutions pour les exemples courants.

I) FONCTIONNEMENT
Dans Game Maker, une collision se passe lorsque 2 éléments se rencontrent. Prenons deux carrés : s’ils sont éloignés, il ne se passe rien, mais dès que les deux carrés se mettent à partager rien qu’un seul pixel, vlan, c’est la collision. Cela signifie déjà que deux objets côte à côte n’entraînent pas de collision, c’est le premier truc important à retenir.

Ca, c’est le principe d’une collision. Pour des carrés, il n’y a pas de problème , comme pour toute forme géométrique de base. Mais parlons un peu des collisions avec les sprites si vous le voulez bien.
Un sprite, par définitions, est plus complexe qu’un simple polygone, calculer une collision est moins aisée, et c’est là que game maker intervient et prend tout, absolument tout en charge. Dans les collisions, vous avez toujours le choix entre ‘precise’ or not precise, tel est la question. Si vous mettez precise à la valeur false, la bounding box du sprite sera utilisée, bref, la collision est simplifiée pour ressembler à un carré.
C’est quand même souvent assez erratique, même si ça optimise la chose. Je ne m’intéresserai qu’à la collision precise, c'est-à-dire prenant exactement la forme du sprite. Game Maker, dans sa grande clémence ne détectera pas les zones transparentes. C’est tout bête, mais faut y penser.
Une fois de plus, rien d’exceptionnel, mais ça arrive : Ce que vous ne savez pas forcément, c’est que les variables préfabriquées commencant par image_*, comme image_angle, image_xscale et tout ça sont controllées au niveau des collisions. Bref, vous étirez votre objet, vous le tournez, les collisions changeront en conséquence, et ça c’est plutôt chouette. Même là, ça ne sautera pas aux yeux de tout le monde, mais ça donne une grande liberté d’action tout ça.

II) PRINCIPALES FONCTIONS
Place_free(x,y)
Surement une des fonctions les plus utilisées lors des collisions. Cette fonction permet de tester virtuellement si l’instance serait en collision avec un objet noté solid si elle était en (x,y). Cette fonction est utilisée par exemple pour voir si un déplacement est possible, en vérifiant si la prochaine position est bien libre de collision avec un objet solid. On peux aussi s’en servir pour voir si l’instance est au sol, en faisant place_free(x,y+1) pour voir si un pixel plus bas, il y a bien quelque chose qui l’empêche de tomber. Cette solution est peu utilisée, et pourtant fonctionne avec moins de problème qu’un position_meeting ou autre. Cette fonction retourne true (si pas de collision) ou false (si il y a collision).

place_empty()
C’est en quelque sorte comme place_free, sauf que cette fonction détecte tout objet, solid ou non.
C’est la fonction à utiliser si pour une raison ou une autre vos murs ne sont pas solids. Mais à part ça, c’est strictement la même fonction que place_free. Cette fonction retourne true (si pas de collision) ou false (si il y a collision).

place_meeting(x,y,obj)
Cette fonction retourne une fois de plus true ou false, et effectue un contrôle comme les deux précédentes, mais pas sur les objets solids ou non, mais sur un objet particulier. Je ne m’attarderai pas dessus, elle est exactement comme les deux autres.

Le problème de ces fonctions, c’est qu’elles retournent juste true et false, alors que lors d’un event collision en drag & drop, on peut utiliser other, et par conséquent connaitre l’id de l’instance en collision. Pour cela, il existe une fonction moins connue mais très précieuse :

Instance_place(x,y,obj)
Cette dernière est exactement comme place_meeting, toujours avec des collisions virtuelles et avec un objet, mais contrairement à la précédente, si vous indiquez un objet, l’id de l’instance de l’objet en collision ressort ! Et ça, c’est tout de même très pratique, ‘faut pas s’en priver, hein…

Il reste les collisions géométriques (collision_line, rectangle, etc.) mais ne concernant pas directement le sprite de votre objet, elle n’ont pas de grande spécificité, je ne les détaillerai pas. Mais cela ne signifie pas qu’elles ne sont pas importantes ! Un bon collision_line de tant en tant ne fait pas de mal :p
NOTE :
Johny Wessmuller m'a fait remarque qu'à présent les fonctions collision_* retournent l'id d'un objet en collision, plus seulement true/false.
Cela dit , il reste le problème suivant: ça retourne une id, mais pas forcément celle du premier objet rencontré. L'exemple typique, c'est lorsqu'on tire sur un petit ennemi accolé à un mur: que faire si la balle détecte le mur?
D'après moi il y a 2 solutions simples à mettre en place:

  • Faire des collision_line() par dichotomie, pour trouver quel est le premier objet touché (voir ça)

  • Une fois une id trouvée, refaire un collision_line entre la balle et le côté de l'id le plus proche, pour voir s'il y avait déjà une collision avant, et répéter jusqu'à avoir un collision_line qui retourne noone ou l'id de la balle.



III) EXEMPLES CONCRETS
1) Quand mon personnage tire, les balles traversent les murs les plus fins….
Normal, car en informatique, la balle passe d’un point à un autre sans passer par les point intermédiaires. Bref, en ayant une speed de neuf, la balle avance d’un coup de 9 et s’en fout de ce qu’il y a entre. Conséquence : s’il y a un mur dans ces pixels, il n’est pas détecté.
Ce qu’on veux, c’est voir s’il y a un mur entre. Rien de plus simple :
Si vous aviez un event collision en drag&drop, ça correspond à ça :
(«code»):
If instance_place(x,y,obj_mur)
{
//vos actions
}

Et bien à la place faites ça :
(«code»):
if collision_line(xprevious,yprevious,x,y,obj_mur,true,true)
{
//vos actions
}

Vous n’aurez plus de problèmes.


Mais si toute fois vous avez besoin de récupérer l’id du mur rencontré, c’est un peu plus compliqué. On se retrouve à faire une boucle faisant des tests avec instance_place tout le long de la ligne. Autant dire que plus la vitesse est grande plus les tests seront nombreux… et cela peut s’avérer long !
Voilà le principe, en agissant avec une balle dont le sprite est orienté à un angle de 0 et dont l’image_angle dépend de la collision… (Bref, on avance de la longueur du sprite à chaque boucle, pour faire le moins de tests possibles tout en ne sautant aucun pixel.)


(«code»):

for( length=0 ; length<=speed ; length+=sprite_width)
{id_mur= instance_place( x+lengthdir_x(length,direction) , y+lengthdir_y(length,direction) , obj_mur )
If id_mur!=noone
{break}
}
//Maintenant, l’id du mur rencontré est stockée dans id_mur. Si’il n’y a pas de mur, id_mur=noone

On peux encore améliorer tout ça. Si la balle ne rencontre pas de mur, c’est bête de faire tous ces tests pour rien. Il suffit alors de commencer par un collision line, et seulement si celui-ci est positif on fera le test avec la boucle.

Voilà, problème résolu, la balle ne traversera plus rien, et vous n’êtes pas incommodé pour autant : l’id de l’objet en collision est retournée aussi bien qu’avec un event de collision !


2) La gravité… houlala…
En plus d’être un bon exemple, ça vous donne un bout de code tout prêt souvent redouté par les codeurs mais sans raison : la gravité.
On va séparer tout ça :


*Le personnage s’arrête en rencontrant un mur quand il tombe.
Pas dur, mais je vous conseil de faire comme cela : les test d’abord, le déplacement après. Sinon, le joueur se déplace trop loin et rentre dans le mur, collision ou pas. Le principe est donc de voir s’il y a collision, et dans ce cas de ne pas avancer normalement mais de s’accoler au mur.
( »code »):
if not place_free(x+hspeed,y+hspeed]
{
gravity=0
vspeed=0
move_contact_solid(direction,speed)
}
else
{
gravity=2
}

Et voila, votre personnage ne rentrera plus dans les murs en tombant. Bien sur, si votre personnage est plus rapide que la hauteur de son sprite (bref, qu’entre chaque step il y a des pixels libres), vous pouvez de nouveau appliquer la technique du collision_line, mais c’est assez rare pour les grands objets .


*Quand le perso se retourne, il se coince dans les murs
Je réponds à ça parce que c’est un problème courant, mais ça n’a rien à voir avec les collision. Il faut juste s’arranger pour que l’origine du sprite soit BIEN AU MILIEU du sprite effectif (on compte pas les marges), et ça se passera très bien. Sinon, déplacez ‘à la main’ le personnage lors d’un retournement.


*Avancer quand c’est possible.
Hum… ça defrait pas être compliquer ça si vous avez suivi jusqu’ ici :p
( »code »):
if keyboard_check(vk_left) && place_free(x-8,y)
{
x-=8
}
else
{move_contact_solid(180,8)}

Pareil pour aller à droite, avec vk_right. De nouveau, au test, PUIS on déplace. Normalement s’il n’y a pas de collision, et sinon jusqu’au mur.


*Sauter quand on est au sol
Dans beaucoup de jeux de plateforme que j’ai vu, l’auteur se sert souvent de vspeed pour savoir s’il peut sauter. Bien sur, en tombant il ne poura pas sauter, mais juste ‘au sommet’ de son saut, vspeed=0, et donc le joueur peut sauter … en l’air :/
Il suffit de vérifier que le player à bien un bloc sous ses pieds… Après tout c’est comme ça que ça se passe, non ?
Essayer de sauter lorsque vous êtes en l’air, vous verrez ^^
Ca donne :
( »code »):
if instance_place(x,y+1,obj_mur) {vspeed=-15}

Alors ? c’était compliqué ?
En quelques lignes de codes, vous avez déjà de quoi déplacer un personnage dans un jeu de plateforme.
Allez, comme je suis inspiré et que vous êtes gentils ( ^^ ) je rajoute une petite partie :

IV) OPTIMISATION
Ce sera pas long. Je parlais de l'importance des vairables image_angle, image_xscale et tout ça.
En effet, si vous avez un mur dont le sprite est uni, laissez lui un sprite de 1*1 avec cette couleur et pour avoir ces dimensions normales (ex : 32*32) vous mettez dans son create event :
("code"):
image_xscale=32
image_yscale=32

Votre bloc aura la même apparence, la même incidence, mais votre jeu aura une taille beaucoup plus petite et sera un peu plus rapide.
De la même manière, c'est un très bon moyen pour faire des blocs de taille changeante : En changeant le scale, le moteur de collision suit! Ce 'détail' est souvent oublié, et pourtant ça permet des choses géniales.
Attention tout de même, parce que faire un bloc qui s'agrandit entrainera le joueur... à l'intérieur de lui même, et c'est pas cool (ah ça non, c'est pas cool.).
Deux possibilités :
Soit, dans le bloc qui s'agrandit vous faites :
('code'):
with(instance_place(x,y,obj_perso))
{y-=6}

Si le bloc monte de 6 bien sur... C'est peut être la solution la plus simple.
Sinon, vous rajoutez un peu de code dans le personnage, pour qu'il sorte tout seul s'il est dans un objet. Cette solution permet aussi d'empêcher d'autres bugs de collision d'apparaitre.

Voilà, vous êtes plus intelligent, ou tout du moins vous savez faire des belles collisions bien mieux que celles en Drag&Drop.

Votre Serviteur, M@D_Doc.
08/02/2008 par M@d_Doc
20 Commentaires

par mark_overmars @ 08/02/2008 08:03 pm
gweat joeuwb mawd dowc tyouw awre deuw bewste super

par malax @ 08/02/2008 09:42 pm
sinon y'a le contact basic: x=xprevious y=yprevious, enfin je sais meme pas ce que c'est un jeu de plate forme gniah

par mad_doc @ 08/02/2008 09:45 pm
jeu de plateforme : jeu vu de coté (comme mario quoi) où le personnage subit la gravité (saut et chute)

... c'est quoi ton contact basic ?!? Je vois pas ce que tu veux dire...

par daminetreg @ 08/02/2008 10:10 pm
Great Job, très bon tuto pour la plupart des GM Users. Félicitations, je pense qu'il va avoir du succès.

par [TheDarkTiger] @ 08/02/2008 10:54 pm
vraiment sympa comme tuto !

et bien réalisé avec ça .

bravo super

par Death Egg @ 10/02/2008 11:12 am
Tuto très sympa! Surtout pour la partie optimisation, j'y aurais jamais pensé! ^^

par Topaze22 @ 10/02/2008 03:34 pm
Nice ! Bien illustré et tout coeur

par daniel @ 12/02/2008 04:47 pm
je ne trouve pas de tutorail pour faire saute un personnage
quelqu'un peut m'aider je suis débutant

par daminetreg @ 13/02/2008 09:22 am
Pour faire un jeu de plateforme, tu as des packs d'exemples pleins de jeux et de moteurs de plateforme.
Tu peux les trouver ici: http://www.lecbna.org/pages/gm6.php
Hésites pas à demander lesquels packs télécharger si tu ne trouves pas. :)

par Dark Gokou @ 20/02/2008 02:10 pm
Super tuto, vraiment très utile, surtout quand on a la flemme de lire la doc. ange

par Red-error @ 01/03/2008 06:58 pm
Eh, pour la partie "optimisation" , il y a un problème...
Rajoute qu'il faut mettre le bounding box à "manual" et pas automatique, et d'ajouter 1 à right et bottom, ou sinon le moteur de collision NE SUIT PAS !
J'ai essayé...

A part ce détail, c'est super, je commence à comprendre cette #@|& de gravité...

par Red-error @ 01/03/2008 09:11 pm
Eh, pour la partie "optimisation" , il y a un problème...
Rajoute qu'il faut mettre le bounding box à "manual" et pas automatique, et d'ajouter 1 à right et bottom, ou sinon le moteur de collision NE SUIT PAS !
J'ai essayé...

A part ce détail, c'est super, je commence à comprendre cette #@|& de gravité...

par mad_doc @ 03/03/2008 08:11 am
lol j'ai dit au debut du tuto que je ne parlerai PAS des bounding box et que je ne les utilise jamais...

par GTK @ 15/03/2008 01:37 pm
Tu dis "En changeant le scale, le moteur de collision suit!", pourtant, dans un de mes projet de style casse-brique, j'utilisais le xscale pour agrandir ma batte mais une fois qu'elle dépassait une certaine taille, la balle avait tendance à passer à travers alors que quand elle avait ça taille originel, elle ne passait jamais a travers et si j'utilisais un sprite plus grand, le problème ne survenais pas ?

par djFRk @ 18/03/2008 05:02 am
J'ai bien lu ton tutorial qui est tres pratique mais j'ai rien trouvé la dedans qui puisse corrigé mon probleme.

je m'explique :
Alors pour commencer je debute !
je creer un casse brick zelda depuis 3 jours qui est plutot sympa pour le moment, selon ceux qui l'ont testé !
Alors mon probleme c'est que quand je rentre en collision avec un sprite ( y'a un event bounce precisely against object dessus ), mon objet ( c'est un boomerang ) va se rendre vers un autre objet qui porte le meme evenement que le precedent et la en faite mon objet ( mon boomerang ) va jouer au ping pong entre c deux sprite d'event bounce ouh !
Voila pour ce bleme je sais pas quoi faire si t'a une solution a me donné ce serait cool ?

ah oui j'oublie!
meme mes events en bounce not precisely ca marche pas !

P.s si des gens veulent bien m'ajouté sur msn pour me donné des coups de mains ce serait pas de refus !

djfrk@hotmail.fr

Voila merci !

par daminetreg @ 22/04/2008 01:10 pm
Ce n'est pas nécessairement un problème de collision que tu as, mais plutôt au niveau des event, l'évènement que tu utilise me paraît un peu hétéroclyte pour ce que tu veux faire, utilises plutôt une event collision.

par Johny Wessmuller @ 28/04/2010 07:29 pm
Super tuto je crois qu'on peut remplacer l'objet obj_mur par solid si obj_mur est solide et comme ça ça marche pour tout les objets solides.
Si vous voulez le faire pour tous les objets , je crois qu'il faut mettre other

par ygh @ 08/02/2011 04:50 pm
gnii rouge super super

par ice shoot @ 05/06/2011 07:51 pm
Bonjour ,

Je cherche comment faire tirer un homme avec son fusil , dans une certaine direction avec une certaine distance . Je ne trouve pas comment le faire . J'ai la version 8.0 payante .

Merci de me repondre vite .

par glook @ 07/01/2012 08:35 pm
je cherche comment créer un monde avec bâtiment en 3d et un perso qu'on peu controller en 3d

Nom:
Mail: (optionel)
Êtes vous Humain? (Entrez oui si c'est le cas)

smile's:

fleche_dfleche_gbehgniihappy1questionrirerire2rougesupertristeangeclincoeurcoleredodofierghagniahhaphehehhontenonnon3ouhouisnif

| M'oublier