Les collisions glissantes avec 3D Games Creator Pro
Un article de Games Creators Network.
Bon, d'abord, une collsion glissante, c'est quoi ?! Imaginez que vous créez deux objets et que vous les approchez l'un de l'autre : ils finiront par se chevaucher. L'idée de la collision glissante est de les faire réagir entre eux de manière à ce qu'ils prennent une consistance solide. En gros, c'est ça.
Et comment ça marche ? Il existe de nombreuses méthodes ; dans ce tutoriel, vous n'en trouverez que quelques unes, mais cela ne signifie pas qu'elles sont forcémment les meilleurs. Si vous en connaissez de bonnes, n'hésitez pas à éditer ce tutoriel.
Sommaire |
[modifier] Une première méthode de collisions
Une méthode très simple consiste à replacer un objet sur ses anciennes coordonnées s'il est en collision après avoir été déplacé, mais le résultat n'est pas terrible. J'expliquerai comment l'améliorer plus loin. En voici un exemple :
set display mode 800,600,32 sync on sync rate 50 rem création du joueur make object cube 1,1 offset limb 1, 0, 0, 0.5, 0 position object 1, rnd(100), 0, rnd(100) x as float y as float z as float angle as float oldx as float oldy as float oldz as float oldangle as float rem création d'un "environnement" make matrix 1, 100, 100, 10, 10 for t=2 to 52 make object box t, 2+rnd(50)*0.1, 1+rnd(20)*0.1, 2+rnd(50)*0.1 color object t, rgb(50+rnd(100), 50+rnd(100), 50+rnd(100)) repeat position object t, rnd(100), object size y(t)*0.5, rnd(100) until object collision(t,0)=0 next t do rem déplacement du joueur oldx = object position x(1) oldy = object position y(1) oldz = object position z(1) oldangle = object angle y(1) if upkey()=1 then move object 1, 0.1 if downkey()=1 then move object 1, -0.1 if leftkey()=1 then yrotate object 1, wrapvalue(object angle y(1)-5) if rightkey()=1 then yrotate object 1, wrapvalue(object angle y(1)+5) x = object position x(1) y = object position y(1) z = object position z(1) angle = object angle y(1) rem gestion des collisions if object collision(1, 0)>0 position object 1, oldx, oldy, oldz endif x = object position x(1) y = object position y(1) z = object position z(1) rem gestion de la caméra set camera to follow x, y, z, object angle y(1), 4, 2, 7, 0 sync loop
Le problème avec ce code, c'est qu'il bloque complètement le joueur lorsqu'il touche un objet. C'est pourquoi il faut développer un système de collisions glissantes : le joueur glisse contre les autres objets.
[modifier] Amélioration : faire glisser le joueur
Au lieu de le replacer directement sur ses anciennes coordonnées, on peut tenter de le faire progressivement, sur chaque axe. Ainsi, si c'était seulement le déplacement sur X qui posait problème, le déplacement sur les deux autres axes est conservé. Cela donne :
rem gestion des collisions if object collision(1, 0)>0 position object 1, oldx, y, z if object collision(1, 0)>0 position object 1, x, y, oldz if object collision(1, 0)>0 position object 1, oldx, y, oldz endif endif endif
Vous remarquerez que je ne tiens ici pas compte de l'axe Y. Il est possible de l'ajouter, mais étant donné qu'on ne peut pas monter sur des boîtes dans mon exemple, cela compliquerait inutilement le code.
[modifier] Amélioration : tenir compte de la rotation du joueur
Le code fonctionne déjà mieux, mais il possède toujours un problème. Essayez d'aller très près d'une boîte et de tourner : le joueur se retrouve bloqué. Ce problème n'est posé que si le joueur n'a pas forme humaine (cylindrique) mais une autre forme, comme une tank par exemple. Il faut donc encore tenir compte des rotations. La solution la plus simple est d'empêcher la rotation si elle provoque une collision. Voici le résultat :
rem gestion des collisions if object collision(1, 0)>0 yrotate object 1, oldangle if object collision(1, 0)>0 position object 1, oldx, y, z if object collision(1, 0)>0 position object 1, x, y, oldz if object collision(1, 0)>0 position object 1, oldx, y, oldz endif endif endif endif
En poussant un peu à l'extrême cette méthode, on arrive au code ci-dessous, mais les différences sont tellement minimes par rapport à la perte de vitesse que ce n'est pas particulièrement conseillé.
rem gestion des collisions if object collision(1, 0)>0 yrotate object 1, oldangle if object collision(1, 0)>0 yrotate object 1, angle position object 1, oldx, y, z if object collision(1, 0)>0 yrotate object 1, oldangle if object collision(1, 0)>0 yrotate object 1, angle position object 1, x, y, oldz if object collision(1, 0)>0 yrotate object 1, oldangle if object collision(1, 0)>0 yrotate object 1, angle position object 1, oldx, y, oldz if object collision(1, 0)>0 yrotate object 1, oldangle endif endif endif endif endif endif endif
Mais le problème avec ce code est que les décors doivent être alignés sur les axes (angle Y égal à zéro), sinon le résultat n'est pas très bon. Il va falloir que je planche un moment dessus.
[modifier] Collisions 2D pour un bomberman
Voici un petit exemple que j'ai écrit. Il montrer comment faire un système de collisions glissantes pour un pacman.
Les explications sont dans le code - si vous pensez qu'il en faudrait plus, n'hésitez pas à en ajouter ou à en demander (sur la page de discussion)
set display mode 1024,768,32 sync on sync rate 50 `création d'une image de mur for t=64 to 32 step -1 ink rgb(128-t,128-t,128-t),0 box 0.2*(64-t),0.2*(64-t),t,t next t get image 1, 0, 0, 64, 64, 1 `création d'une image de béton ink RGB(100,100,120),0 box 0,0,64,64 for t=1 to 50 dot rnd(54),rnd(64),rgb(70+rnd(60), 70+rnd(60), 90+rnd(60)) next t get image 2, 0, 0, 64, 64, 1 `création d'une image d'herbe ink RGB(0,128,64),0 box 0,0,64,64 for t=1 to 50 dot rnd(54),rnd(64),rgb(rnd(15), 128+rnd(64), 64+rnd(32)) next t get image 3, 0, 0, 64, 64, 1 ink rgb(255,255,255),0 `création d'une image d'eau ink RGB(0,0,200),0 box 0,0,64,64 for t=1 to 100 dot rnd(54),rnd(64),rgb(0,0,rnd(200)) next t get image 4, 0, 0, 64, 64, 1 ink rgb(255,255,255),0 `chargement du sol type t_ground image mouvement as float endtype dim ground(16,12) as t_ground restore _img for y=1 to 12 for x=1 to 16 `on lit le type de sol read texture ground(x,y).image = texture `traitement des différents types de collision : `la vitesse à travers le milieu sera multipliée `par cette valeur. `attention, le test est effectué sur deux côtés du `joueur, il peut donc être à moitié ou entièrement `ralenti, d'où la nécessité de mettre la racine de `la valeur if texture = 1 then ground(x,y).mouvement = 0 if texture = 2 then ground(x,y).mouvement = 1 if texture = 3 then ground(x,y).mouvement = sqrt(0.5) if texture = 4 then ground(x,y).mouvement = sqrt(0.05) next x next y `variables joueur type t_joueur x as float y as float vmax as float v as float taille as float endtype joueur as t_joueur `deux-trois valeurs pour le joueur joueur.x = 512 joueur.y = 128 joueur.vmax = 6.25 joueur.taille = 16 do `affichage de la carte for x=1 to 16 for y=1 to 12 paste image ground(x,y).image, 64*(x-1), 64*(y-1), 1 next y next x set cursor 0,0 `déplacement du joueur if leftkey()=1 `il faut trouver quels serait la case de gauche si le `joueur se déplaçait de joueur.vmax dans cette direction ground_gauche_x = (joueur.x-joueur.taille-joueur.vmax)/64 + 1 `sur Y, il peut y avoir deux possibilités... ground_gauche_y1 = (joueur.y-joueur.taille)/64 + 1 ground_gauche_y2 = (joueur.y+joueur.taille)/64 + 1 `multiplication de vmax par les valeurs des cases de gauche... joueur.v = joueur.vmax * (ground(ground_gauche_x, ground_gauche_y1).mouvement+ground(ground_gauche_x, ground_gauche_y2).mouvement)*0.5 `déplacement dec joueur.x, joueur.v endif if rightkey()=1 `il faut tester les collisions sur les deux coins de gauche... ground_gauche_x = (joueur.x+joueur.taille+joueur.vmax)/64 + 1 ground_gauche_y1 = (joueur.y-joueur.taille)/64 + 1 ground_gauche_y2 = (joueur.y+joueur.taille)/64 + 1 `multiplication de vmax... joueur.v = joueur.vmax * (ground(ground_gauche_x, ground_gauche_y1).mouvement+ground(ground_gauche_x, ground_gauche_y2).mouvement)*0.5 `déplacement inc joueur.x, joueur.v endif if upkey()=1 `il faut tester les collisions sur les deux coins de gauche... ground_gauche_x1 = (joueur.x+joueur.taille)/64 + 1 ground_gauche_x2 = (joueur.x-joueur.taille)/64 + 1 ground_gauche_y = (joueur.y-joueur.taille-joueur.vmax)/64 + 1 `multiplication de vmax... joueur.v = joueur.vmax * (ground(ground_gauche_x1, ground_gauche_y).mouvement+ground(ground_gauche_x2, ground_gauche_y).mouvement)*0.5 `déplacement dec joueur.y, joueur.v endif if downkey()=1 `il faut tester les collisions sur les deux coins de gauche... ground_gauche_x1 = (joueur.x+joueur.taille)/64 + 1 ground_gauche_x2 = (joueur.x-joueur.taille)/64 + 1 ground_gauche_y = (joueur.y+joueur.taille+joueur.vmax)/64 + 1 `multiplication de vmax... joueur.v = joueur.vmax * (ground(ground_gauche_x1, ground_gauche_y).mouvement+ground(ground_gauche_x2, ground_gauche_y).mouvement)*0.5 `déplacement inc joueur.y, joueur.v endif `affichage joueur box joueur.x-joueur.taille, joueur.y-joueur.taille, joueur.x+joueur.taille, joueur.y+joueur.taille sync loop `données de la carte _img: data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 data 1,3,3,3,3,3,3,3,3,2,2,2,2,2,3,1 data 1,2,2,2,2,2,2,3,3,2,3,4,4,3,3,1 data 1,3,3,3,3,3,2,3,3,2,3,4,4,3,3,1 data 1,3,3,3,3,3,2,2,2,2,2,2,2,2,2,1 data 1,3,3,3,3,3,2,3,3,3,3,3,3,3,2,1 data 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1 data 1,2,2,2,2,3,2,3,1,3,3,3,3,3,2,1 data 1,2,3,1,2,3,2,3,1,3,2,3,3,3,2,1 data 1,2,3,1,2,2,2,3,1,3,2,2,2,2,2,1 data 1,2,3,1,3,3,3,3,1,3,3,3,3,3,3,1 data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

