Les collisions glissantes avec 3D Games Creator Pro - Games Creators Network
Article     Discussion     Modifier     Historique     Forums     Salon IRC

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

 

Rechercher
Installer l'extension de recherche Plus d'informations

 

Comprendre
Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. - Benjamin Franklin

 

Partager
La connaissance est la seule chose qui s'accroit lorsqu'on la partage. - Sacha Boudjema

 

Créer
L'imagination est plus importante que la connaissance. - Albert Einstein

 

 

Le wiki en images Le wiki en images Image du mois: «Snowball: un prototype de jeu développé avec NeL.