Outils pour utilisateurs

Outils du site


billard_boules

Une table de billard avec des boules qui s'entrechoquent

Principes

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:

  • la largeur (defaut=800 pixels) ou la hauteur (defaut=400 pixels) de la zone de déplacement des boules.
  • le nombre de boules à placer (defaut=10) de 1 à ??. La valeur maxi est atteinte quand l'initialisation est bloquée parce qu'il n'y a plus de place pour mettre une boule de plus.
  • la temporisation pour ralentir plus ou moins le déplacement des boules (défaut=0.0005). Cela dépend de la taille de la zone, du nombre de boules et de la puissance de votre ordinateur.

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:

Codage

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!"
 

billard_boules.txt · Dernière modification: 2008/05/18 13:38 par tyrtamos

Outils de la page