© Your Copyright
L’objectif de ce cours est de vous permettre de concevoir un algorithme de gestion des collisions entre les objets que vous animerez dans votre jeu. Trois stratégie vous sont proposées. Vous devez être capable de les intégrer dans un projet au besoin.
L’objectif précédent vous a appris à déplacer des objets dans un espace en 2 dimensions au moyen d’intégration de lois plus ou moins physiques avec la méthode d’Euler. Maintenant, il s’agit de stopper la trajectoire de ces objets lorsqu’ils rencontrent un obstacle... voire même de pouvoir rebondir sur ces obstacles. Pour cela il faut détecter et gérer les collisions.
Détection des collisions
La détection des collisions est un mécanisme permettant de repérer des déplacements interdits ou nécessitant un traitement particulier, au cours de la simulation d’objets en mouvement.
On peut effectuer la détection et le traitement des collisions à chaque petit déplacement des éléments à déplacer ou une seule fois par pas de simulation.
Pour réaliser la détection de collisions, il faut concevoir une ou plusieurs fonctions de détections de collisions. Un algorithme de détection des collisions s’appuie sur des données qui décrivent les éléments pouvant entrer en collision. Il faut donc représenter (ou modéliser) les “obstacles” et les objets en mouvement.
Soyez astucieux! La barrière d’abstraction peut vous aider.
Vos connaissances mathématiques vous permettent de mettre en équations un certain nombre de formes géométriques. Vous pouvez par exemple mettre en équation des demi-plans dans un espace en 2D et délimiter des zones par intersection de ces demi plan grâce à un système d’inéquations :
Mise en équation d’un triangle isocèle rectangle
Voici la mise en équation d’un triangle isocèle rectangle:
\(\left\{\begin{array}{l}\displaystyle x > 0 \\ \displaystyle y > 0 \\ \displaystyle y < -x+1 \\ \end{array} \right.\)
Mise en équation d’un disque
Voici la mise en équation d’un disque:
\((x-x_{centre})^2+(y-y_{centre})^2 < rayon^2\)
Gestion des collisions dans un rectangle
\(\left\{\begin{array}{l}\displaystyle 0 > x > x_{max} \\ \displaystyle 0 >y > y{max} \\ \end{array} \right.\)
def move(dt,balle,xMax,yMax):
x0,y0=balle.position
vx,vy=balle.vitesse
x1=x0+dt*vx
y1=y0+dt*vy
balle.position = x1,y1
collide(balle,xMax,yMax)
def collide(balle,xMax,yMax):
x,y=balle.position
vx,vy=balle.vitesse
if(x<0):
x=0
vx=-vx
if(y<0):
y=0
vy=-vy
if(x>xMax):
x=xMax
vx=-vx
if(y>yMax):
y=yMax
vy=-vy
balle.position=x,y
balle.vitesse=vx,vy
La mise en équation se limite à des environnements assez simples pour que vous puissiez les mettre en équations. Cette méthodes est souvent la plus efficace en terme de temps de calcul mais elle s’adapte difficilement aux environnements dynamique dont la forme évolue au cours du temps.
Une balle se déplace dans une pièce carré. Au centre de la pièce un mur carré bloque la pièce. Faites rebondir la balle sur les murs en complétant la fonction move()
Tester la si la futur position occupée correspond à une case vide dans la grille.
def move(ball,dt,c1,c2):
#c1=cote carre interieur
#c2=cote carre exterieur
#coin inferieur gauche de c1 en 1,1
x=ball.p[0]+ball.v[0]*dt
y=ball.p[1]+ball.v[1]*dt
dontmove=False
#carre interieur
xmini=c1/2.0-c2/2.0
xmaxi=c1/2.0+c2/2.0
ymini=c1/2.0-c2/2.0
ymaxi=c1/2.0+c2/2.0
#position future dans le carre?
if x>xmini and x<xmaxi and y>ymini and y<ymaxi:
#si la position futur est dans le carre central
dontmove=True
#gestion collision
if ball.p[0]<xmini or ball.p[0]>xmaxi:
ball.v[0]=-ball.v[0]
else:
ball.v[1]=-ball.v[1]
else:
#carre exterieur
if x<0 or x>c1:
dontmove=True
ball.v[0] = -ball.v[0]
if (y<0 or y>c1):
dontmove=True
ball.v[1] = - ball.v[1]
if dontmove==False:
ball.p=[x,y]
Cette méthode s’appuie sur la précédente. Il s’agit en fait de composer un environnement d’éléments pour lesquels on sait détecter les collisions et d’appliquer la gestion des collisions pour chacun de ces élément.
Par exemple, il peut paraître difficile de mettre en équations un labyrinthe. Mais, il est facile de gérer les collisions entre un rectangle et un personnage qui se déplace en 2D. Si on construit le labyrinthe comme un somme de rectangles placés dans l’espace, pour gérer les collisions personnage/environnement, il suffit de répéter le calcul élémentaire de gestion de collisions personnage/rectangle pour chaque rectangle composant le labyrinthe.
fireZones
On imagine qu’il y a un type abstrait FireZone qui définit une zone enflammée qui fait perdre des point de vie au joueur s’il s’y trouve. La forme de cette zone est élémentaire, par exemple un carré. La fonction FireZone.isInside() effectue un calcul pour indiquer si une position se trouve ou non dans le carré.
def move(dt, player, fireZones):
x0,y0=player.position
vx,vy=player.vitesse
x1=x0+dt*vx
y1=y0+dt*vy
player.position = x1,y1
collide(player, fireZones)
def collide(player, fireZones):
x,y=player.position
for f in fireZones:
#detection
if fireZone.isInside(f,x,y):
#gestion
player.health=player.health-1
Pour gérer les collision Joueur/zones enflammé on a donc tester successivement la collision avec chaque zone.
La limite de cette méthode est qu’elle peut amener à réaliser un grand nombre de calculs inutiles. En effet si les collections d’obstacles à gérer son très importantes, il faudra probablement tester les collisions sur l’ensemble des obstacles...
Plus que pour gérer des collision entre personnage et environnement, cette méthode est intéressante pour gérer des collisions entre objets mobiles ou personnages mobiles.
Une balle se déplace dans un triangle. Faites rebondir la balle sur les murs en complétant la fonction move()
Tester la si la futur position occupée correspond à une case vide dans la grille.
La limite de la méthode précédente est qu’on est amené à tester des collisions entre des objets pouvant être très éloignés et n’avoir aucune chance d’entrer en collision dans l’état actuel du jeu. Il est possible de considérablement optimiser les recherches de collisions en utilisant un maillage de l’espace.
Principe: L’affichage et la simulation se basent sur une représentation partagée du “monde” simulé. Cette représentation peut être une grille dans laquelle on insère les différents éléments du monde.
world=[]
world.append(['+','-','-','-','-','-','+'])
world.append(['|',' ',' ',' ',' ',' ','|'])
world.append(['+','-','+',' ',' ',' ','|'])
world.append([' ',' ','|',' ',' ',' ','|'])
world.append([' ',' ','+','-','-','-','+'])
#player.py
def move(dx,dy,player,w):
x,y=player.position
x=x+dt
y=y+dy
if (world.isValide(w,x,y)):
player.position=x,y
#world.py
def isValid(w,x,y):
if (w[int(y)][int(x)]==' '):
return True
else:
return False
Un personnage sur déplace aléatoirement dans une pièce. Empêchez le de traverser les murs.
Tester la si la futur position occupée correspond à une case vide dans la grille.
grid=["***********","* * *","* * *","* *","* * *","* * *","* * *","* * *","***********"]
class Player: pass
player=Player()
player.x=1
player.y=1
def move(player,grid):
x=playe.x+random.randint(0,2)-1
y=player.y+random.randint(0,2)-1
if (grid[y][x])==" ":
player.x=x
player.y=y
drawBg(grid)
i=1000
#boucle de simulation
while(i):
i=i-1
move(player,grid)
show(player)
Reprenez l’exemple de base animat en utilisant le fond d’écran pour les collisions.