On fabrique des boules en quantité, taille, vitesse et direction de départ au hasard.
On lance le déplacement des boules.
On teste à chaque déplacement de chaque boule si cette boule rentre en collision avec une autre boule ou avec les bords de la fenêtre, et on corrige la trajectoire en conséquence.
On peut changer à l'appel de l'application:
Bien entendu, on peut aussi, en intervenant dans le code, modifier la taille des boules.
Voilà la tête que ça a avec les valeurs par défaut:
Et voilà le code. Il est largement auto-documenté.
Pour l'essayer, il vous suffit de copier-coller le code ci-dessous et de le lancer sur votre ordinateur (Windows ou Linux, peut-être Mac) avec Python et Tkinter d'installés.
#!/usr/bin/python # -*- coding: utf-8 -*- from __future__ import division from Tkinter import * import time import random from math import * class Application(Frame): def __init__(self, master=None, larg=800, haut=400, nboules=10, tempo=0.0005): Frame.__init__(self, master) # sauvegarde des paramètres self.master = master self.larg = larg self.haut = haut self.nboules = nboules self.tempo = tempo # création d'un layout_manager sur self self.grid() # création du canvas self.can = Canvas(self, width=self.larg, height=self.haut, bg='ivory') self.can.grid(sticky="nsew") # Création des "nboules" boules et enregistrement dans la liste self.boules self.boules = [] for i in xrange(0,self.nboules): r=random.randint(15,30) # = rayon de la boule dz = random.uniform(0.5,2.5) # = pas d'avancement de la boule c = random.uniform(0,2*pi) # = direction d'avancement (de 0 à 2*pi radians) dy, dx = dz*sin(c), dz*cos(c) # = incréments d'avancement selon x et y # trouver pour la boule un emplacement qui ne soit pas déjà pris par une boule précédente while True: x=random.randint(r,self.larg-r) y=random.randint(r,self.haut-r) ok=True for boule2,x2,y2,dx2,dy2,r2 in self.boules: if sqrt((x2-x)**2+(y2-y)**2)<r+r2: ok=False if ok: break # attention: changement d'ordonnées nécessaire: dans le canevas, l'axe d'ordonnée va vers le bas # en plus: (y,x) est le centre de la boule, mais les paramètres à passer sont différents boule = self.can.create_oval(x-r, self.haut-(y+r), x+r, self.haut-(y-r), width=2, fill='red') # enregistrer la nouvelle boule définie dans la liste self.boules self.boules.append([boule,x,y,dx,dy,r]) def direction(self, dy, dx): """donne l'angle de 0 à 2*pi donné par les incréments (signés) dy et dx""" a = atan(abs(dy/dx)) if dy>=0: if dx>=0: pass else: a = pi-a else: if dx>=0: a = 2*pi-a else: a = pi+a return a def demarre(self): """lancement""" while True: try: for i,(boule,x,y,dx,dy,r) in zip(range(0,len(self.boules)),self.boules): # calcul de la position suivante de la boule i x, y = x+dx, y+dy # corrige la trajectoire si collisions avec les autres boules for j,(boule2,x2,y2,dx2,dy2,r2) in zip(range(0,len(self.boules)),self.boules): if j!=i: # calcul de la distance entre les centres des 2 boules i et j db=sqrt((x2-x)*(x2-x)+(y2-y)*(y2-y)) # test pour savoir s'il y a collision avec une autre boule if db<=r+r2: # oui, il y a collision de la boule i avec la boule j # recul de la boule i pour ne pas avoir de recouvrement avec la boule j x, y = x-dx, y-dy # direction prise actuellement par la boule i (de 0 à 2*pi radians) a = self.direction(dy,dx) # direction donnée par la ligne des 2 boules à partir de la boule i (de 0 à 2*pi radians) b = self.direction(y2-y, x2-x) # nouvelle direction que doit prendre la boule i après rebond sur la boule j (de 0 à 2*pi radians) c = (2*a-b+(2*pi)) % (2*pi) # calcul du nouvel incrément dx et dy de la boule i selon la nouvelle direction c dz = sqrt(dy*dy+dx*dx) dy, dx = dz*sin(c), dz*cos(c) # corrige la trajectoire en fonction des collisions avec les bords if x>self.larg-r: x = self.larg-r dx = -abs(dx) if y>self.haut-r: y = self.haut-r dy = -abs(dy) if x<r: x = r dx = abs(dx) if y<r: y = r dy = abs(dy) # dessine la boule à la nouvelle position (voir remarque plus haut concernant le dessin des boules) self.can.coords(boule, x-r, self.haut-(y+r), x+r, self.haut-(y-r)) # enregistre les nouvelles données de la boule i self.boules[i]=[boule,x,y,dx,dy,r] # temporisation pour ralentir l'affichage time.sleep(self.tempo) # rafraichir l'affichage self.master.update() except: break ########## => programme principal: if __name__=='__main__': try: fen = Tk() fen.title('Jeu fou du chien') app = Application(fen) # possibilité de modif des paramètres. Par exemple: app = Application(fen,600,600,5,0.001) fen.after(50,app.demarre) fen.mainloop() except: fen.destroy() finally: print "bye!"