importpygame,sysimporttimefrompygame.localsimport*LARGEUR=640HAUTEUR=480RAYON=20pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])x=300y=200dx=4dy=-3couleur=(45,170,250)whileTrue:fenetre.fill([0,0,0])pygame.draw.circle(fenetre,couleur,(x,y),RAYON)x+=dxy+=dypygame.display.update()# routine pour pouvoir fermer «proprement» la fenêtre Pygameforeventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.1)
Modifiez le code précédent afin que la balle rebondisse sur chaque paroi (il suffit de modifier intelligemment "les variables de vitesse" dx et dy).
Variables de vitesse
Il s'agit d'un abus de langage. En effet, la vitesse horizontale de la balle se note \(v_x\) pas \(dx\) mais \(v_x\) est liée à la variation de position horizontale \(dx\) par la formule \(v_x=\frac{dx}{dt}\) où \(dt\) est le temps entre deux rafraîchissement d'image que l'on indique dans time.sleep(dt). Donc, à rafraîchissement d'image constant, c'est bien \(dx\) qui permet de fixer la vitesse horizontale de la balle. Il en est de même pour la relation entre la vitesse verticale \(v_y\) et dy.
importpygame,sysimporttimefrompygame.localsimport*LARGEUR=640HAUTEUR=480RAYON=20pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])x=300y=200dx=4dy=-3couleur=(45,170,250)whileTrue:fenetre.fill([0,0,0])pygame.draw.circle(fenetre,couleur,(x,y),RAYON)x+=dxy+=dyif(y<=RAYON)or(y>=HAUTEUR-RAYON):dy=-dyif(x<=RAYON)or(x>=LARGEUR-RAYON):dx=-dxpygame.display.update()# routine pour pouvoir fermer «proprement» la fenêtre Pygameforeventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.02)
importpygame,sysimporttimefrompygame.localsimport*LARGEUR=640HAUTEUR=480RAYON=20pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])dxA=7dyA=4dxB=-5dyB=3xA=LARGEUR//3yA=HAUTEUR//2xB=LARGEUR//2yB=HAUTEUR//2couleurA=(45,170,250)couleurB=(155,17,250)whileTrue:####################### À vous ####################### pygame.display.update()# routine pour pouvoir fermer «proprement» la fenêtre Pygameforeventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.03)
importpygame,sysimporttimefrompygame.localsimport*LARGEUR=640HAUTEUR=480RAYON=20pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])dxA=7dyA=4dxB=-5dyB=3xA=LARGEUR//3yA=HAUTEUR//2xB=LARGEUR//2yB=HAUTEUR//2couleurA=(45,170,250)couleurB=(155,17,250)whileTrue:fenetre.fill([0,0,0])pygame.draw.circle(fenetre,couleurA,(xA,yA),RAYON)pygame.draw.circle(fenetre,couleurB,(xB,yB),RAYON)xA+=dxAyA+=dyAxB+=dxByB+=dyB# rebond en haut ou en basif(yA<RAYON)or(yA>HAUTEUR-RAYON):dyA=-dyA# rebond à gauche ou à droiteif(xA<RAYON)or(xA>LARGEUR-RAYON):dxA=-dxA# rebond en haut ou en basif(yB<RAYON)or(yB>HAUTEUR-RAYON):dyB=-dyB# rebond à gauche ou à droiteif(xB<RAYON)or(xB>LARGEUR-RAYON):dxB=-dxBpygame.display.update()# routine pour pouvoir fermer «proprement» la fenêtre Pygameforeventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.03)
1.3 Gestion de la collision entre les deux balles⚓︎
Q1. À l'aide d'un schéma (papier-crayon !), mettez en évidence le test devant être réalisé pour détecter une collision.
indice
Q2. Implémentez ce test (en créant pour cela une fonction distance ) et affichez "collision" en console lorsque les deux balles se touchent.
importpygame,sysimporttimefrompygame.localsimport*LARGEUR=640HAUTEUR=480RAYON=20pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])dxA=7dyA=4dxB=-5dyB=3xA=LARGEUR//3yA=HAUTEUR//2xB=LARGEUR//2yB=HAUTEUR//2couleurA=(45,170,250)couleurB=(155,17,250)defdistanceAB(xA,yA,xB,yB):return((xA-xB)**2+(yA-yB)**2)**0.5whileTrue:fenetre.fill([0,0,0])pygame.draw.circle(fenetre,couleurA,(xA,yA),RAYON)pygame.draw.circle(fenetre,couleurB,(xB,yB),RAYON)xA+=dxAyA+=dyAxB+=dxByB+=dyB# rebond en haut ou en basif(yA<RAYON)or(yA>HAUTEUR-RAYON):dyA=-dyA# rebond à gauche ou à droiteif(xA<RAYON)or(xA>LARGEUR-RAYON):dxA=-dxA# rebond en haut ou en basif(yB<RAYON)or(yB>HAUTEUR-RAYON):dyB=-dyB# rebond à gauche ou à droiteif(xB<RAYON)or(xB>LARGEUR-RAYON):dxB=-dxBifdistanceAB(xA,yA,xB,yB)<2*RAYON:print("collision")pygame.display.update()# routine pour pouvoir fermer «proprement» la fenêtre Pygameforeventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.03)
Q3. Pour donner l'illusion physique du rebond, échangez les valeurs respectives de dx et dy pour les deux balles.
importpygame,sysimporttimefrompygame.localsimport*LARGEUR=640HAUTEUR=480RAYON=20pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])dxA=7dyA=4dxB=-5dyB=3xA=LARGEUR//3yA=HAUTEUR//2xB=LARGEUR//2yB=HAUTEUR//2couleurA=(45,170,250)couleurB=(155,17,250)defdistanceAB(xA,yA,xB,yB):return((xA-xB)**2+(yA-yB)**2)**0.5whileTrue:fenetre.fill([0,0,0])pygame.draw.circle(fenetre,couleurA,(xA,yA),RAYON)pygame.draw.circle(fenetre,couleurB,(xB,yB),RAYON)xA+=dxAyA+=dyAxB+=dxByB+=dyB# rebond en haut ou en basif(yA<RAYON)or(yA>HAUTEUR-RAYON):dyA=-dyA# rebond à gauche ou à droiteif(xA<RAYON)or(xA>LARGEUR-RAYON):dxA=-dxA# rebond en haut ou en basif(yB<RAYON)or(yB>HAUTEUR-RAYON):dyB=-dyB# rebond à gauche ou à droiteif(xB<RAYON)or(xB>LARGEUR-RAYON):dxB=-dxBifdistanceAB(xA,yA,xB,yB)<2*RAYON:dxA,dxB=dxB,dxAdyA,dyB=dyB,dyApygame.display.update()# routine pour pouvoir fermer «proprement» la fenêtre Pygameforeventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.03)
1.4 Rajout d'une troisième balle et gestion du rebond avec les deux autres.⚓︎
... vraiment ? Peut-on continuer comme précédemment ?
2. La POO à la rescousse : création d'une classe Balle⚓︎
L'objectif est que la méthode constructeur dote chaque nouvelle balle de valeurs aléatoires : abscisse, ordonnée, vitesse, couleur...
Pour l'aléatoire, on pourra utiliser randint(a, b) qui renvoie un nombre pseudo-aléatoire entre a et b.
Il faut pour cela importer la fonction, par from random import randint
Vous pouvez aussi doter votre classe Balle d'une méthode dessine (qui affiche la balle), ainsi qu'une méthode bouge qui la fait bouger.
importpygame,sysimporttimefrompygame.localsimport*fromrandomimportrandint# randint(0,10) -> nb aléatoire entre 0 et 10LARGEUR=640HAUTEUR=480RAYON=20pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])classBalle:def__init__(self):# à vousdefdessine(self):# à vousdefbouge(self):# à vousma_balle=Balle()whileTrue:fenetre.fill([0,0,0])ma_balle.dessine()ma_balle.bouge()pygame.display.update()foreventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.05)
importpygame,sysimporttimefrompygame.localsimport*fromrandomimportrandint# randint(0,10) -> nb aléatoire entre 0 et 10LARGEUR=640HAUTEUR=480RAYON=20pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])classBalle:def__init__(self):self.x=randint(0,LARGEUR)self.y=randint(0,HAUTEUR)self.dx=randint(2,5)self.dy=randint(2,5)self.couleur=(randint(0,255),randint(0,255),randint(0,255))self.taille=RAYONdefdessine(self):pygame.draw.circle(fenetre,self.couleur,(self.x,self.y),self.taille)defbouge(self):self.x+=self.dxself.y+=self.dyifself.y<self.tailleorself.y>HAUTEUR-self.taille:self.dy=-self.dyifself.x<self.tailleorself.x>LARGEUR-self.taille:self.dx=-self.dxma_balle=Balle()whileTrue:fenetre.fill([0,0,0])ma_balle.dessine()ma_balle.bouge()pygame.display.update()foreventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.05)
importpygame,sysimporttimefrompygame.localsimport*fromrandomimportrandint# randint(0,10) -> nb aléatoire entre 0 et 10LARGEUR=640HAUTEUR=480RAYON=20NB_BALLES=10pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])classBalle:def__init__(self):self.x=randint(0,LARGEUR)self.y=randint(0,HAUTEUR)self.dx=randint(2,5)self.dy=randint(2,5)self.couleur=(randint(0,255),randint(0,255),randint(0,255))self.taille=RAYONdefdessine(self):pygame.draw.circle(fenetre,self.couleur,(self.x,self.y),self.taille)defbouge(self):self.x+=self.dxself.y+=self.dyifself.y<self.tailleorself.y>HAUTEUR-self.taille:self.dy=-self.dyifself.x<self.tailleorself.x>LARGEUR-self.taille:self.dx=-self.dxmon_sac_a_balles=# à vous whileTrue:fenetre.fill([0,0,0])forballeinmon_sac_a_balles:balle.dessine()balle.bouge()pygame.display.update()foreventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.05)
importpygame,sysimporttimefrompygame.localsimport*fromrandomimportrandint# randint(0,10) -> nb aléatoire entre 0 et 10LARGEUR=640HAUTEUR=480RAYON=20NB_BALLES=10pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])classBalle:def__init__(self):self.x=randint(0,LARGEUR)self.y=randint(0,HAUTEUR)self.dx=randint(2,5)self.dy=randint(2,5)self.couleur=(randint(0,255),randint(0,255),randint(0,255))self.taille=RAYONdefdessine(self):pygame.draw.circle(fenetre,self.couleur,(self.x,self.y),self.taille)defbouge(self):self.x+=self.dxself.y+=self.dyifself.y<self.tailleorself.y>HAUTEUR-self.taille:self.dy=-self.dyifself.x<self.tailleorself.x>LARGEUR-self.taille:self.dx=-self.dxmon_sac_a_balles=[Balle()for_inrange(NB_BALLES)]whileTrue:fenetre.fill([0,0,0])forballeinmon_sac_a_balles:balle.dessine()balle.bouge()pygame.display.update()foreventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.05)
importpygame,sysimporttimefrompygame.localsimport*fromrandomimportrandint# randint(0,10) -> nb aléatoire entre 0 et 10LARGEUR=640HAUTEUR=480RAYON=20NB_BALLES=10pygame.display.init()fenetre=pygame.display.set_mode((LARGEUR,HAUTEUR))fenetre.fill([0,0,0])classBalle:def__init__(self):self.x=randint(0,LARGEUR)self.y=randint(0,HAUTEUR)self.dx=randint(2,5)self.dy=randint(2,5)self.couleur=(randint(0,255),randint(0,255),randint(0,255))self.taille=RAYONdefdessine(self):pygame.draw.circle(fenetre,self.couleur,(self.x,self.y),self.taille)defbouge(self):self.x+=self.dxself.y+=self.dyifself.y<self.tailleorself.y>HAUTEUR-self.taille:self.dy=-self.dyifself.x<self.tailleorself.x>LARGEUR-self.taille:self.dx=-self.dxforballeinmon_sac_a_balles:if((self.x-balle.x)**2+(self.y-balle.y)**2)**0.5<self.taille+balle.taille:self.dx,balle.dx=balle.dx,self.dxself.dy,balle.dy=balle.dy,self.dymon_sac_a_balles=[]for_inrange(NB_BALLES):new_ball=Balle()mon_sac_a_balles.append(new_ball)# ces 4 dernière lignes peuvent s'écrire par une seule liste en compréhension :# mon_sac_a_balles = [Balle() for _ in range(NB_BALLES)]whileTrue:fenetre.fill([0,0,0])forballeinmon_sac_a_balles:balle.dessine()balle.bouge()pygame.display.update()foreventinpygame.event.get():ifevent.type==pygame.QUIT:pygame.display.quit()sys.exit()time.sleep(0.05)