Correction : introduction à la POO
Une "bulle" qui monte...
import poorpyxel as ppx
from random import randint
ppx.init(150, 150)
def update():
global x, y, vitesse
# Déplacement de la bulle vers le haut en tenant compte de sa vitesse;
# Quand la bulle arrive en haut de la fenêtre, on réinitialise ses caractéristiques.
y = y - vitesse
if y < 0:
y = ppx.height
def draw():
# 1. Effacement global de la fenêtre :
ppx.cls(0)
# 2. Dessin de la bulle dans la fenêtre :
ppx.circ(x, y, rayon, couleur)
# initialisation des caractéristiques de la bulle
x = randint(10, 140)
y = ppx.height
couleur = randint(1, 15)
rayon = randint(2 ,5)
vitesse = randint(2, 10)
ppx.run(update, draw)
Deux choses pour gérer le mouvement de la bulle :
- à chaque "tour" de la boucle d'animation (= à chaque appel de la fonction
update), modifier l'ordonnée y de la bulle pour faire "monter" cette dernière : il faut donc décrémenter la variable y ( ligne 11 ). - tester ( ligne 12 ) si
y < 0, c'est à dire si la bulle "sort" de l'écran par le haut, et, dans ce cas, réinitialiser les caractéristiques la bulle.
Une remarque sur la condition à tester pour "savoir" si la bulle atteint le sommet : on ne peut pas utiliser une simple égalité, car, selon la valeur de la variable vitesse, la variable y n'obtiendra peut-être jamais la valeur 0; il faut donc tester une inégalité qui, elle, sera forcément vérifiée à un moment ou un autre, quelle que soit la valeur de vitesse.
Deux bulles...
Facile : on recopie les variables et les instructions pour gérer le mouvement de la bulle, et on les réutilise avec de nouveaux noms :
import poorpyxel as ppx
from random import randint
ppx.init(150, 150)
def update():
global x, y, vitesse, x2, y2, vitesse2
y = y - vitesse
if y < 0:
y = ppx.height
y2 = y2 - vitesse2
if y2 < 0:
y2 = ppx.height
def draw():
ppx.cls(0)
ppx.circ(x, y, rayon, couleur)
ppx.circ(x2, y2, rayon2, couleur2)
# 1ère bulle
x = randint(10, 140)
y = ppx.height
couleur = randint(1, 15)
rayon = randint(2 ,5)
vitesse = randint(2, 10)
# 2ème bulle
x2 = randint(10, 140)
y2 = ppx.height
couleur2 = randint(1, 15)
rayon2 = randint(2 ,5)
vitesse2 = randint(2, 10)
ppx.run(update, draw)
Trois, quatre,... bulles...
Argh....
Cette façon d'envisager le programme devient de plus en plus délicate à gérer :
- le code devient parfaitement illisible.
- on fait X fois pratiquement la même chose, X étant le nombre de bulles : le code est lourd, pas optimisé,
- si on veut modifier par exemple l'intervalle dans lequel la taille d'une bulle doit se trouver, il faut le faire X fois dans le code pour chacune des bulles ! Le code n'est donc pas maintenable, donc pas fiable car entraînant une grande probabilité d'erreur, surtout si quelqu'un d'autre prenait votre relève et essayait de le développer ( bonne chance alors ! )
D'où la nécessité de passer, dans cette situation, à une autre façon de concevoir le programme, un autre paradigme de programmation, la Programmation Orientée Objet ( POO ).
Le "moule" : la classe Bulle
class Bulle:
# constructeur
def __init__(self,x, y, couleur, taille, vitesse):
self.x = x
self.y = y
self.couleur = couleur
self.rayon = rayon
self.vitesse = vitesse
def afficher(self):
ppx.circ(self.x, self.y, self.taille, self.couleur)
def monter(self):
self.y = self.y - self.vitesse
Pour chaque méthode ou attribut d'un objet, il faut préfixer son nom par self.
Création d'un objet à partir d'une classe
import poorpyxel as ppx
from random import randint
...
class Bulle:
...
def update():
bulle.monter()
def draw():
ppx.cls(0) # effacement de l'écran
bulle.afficher() # affichage de la bulle
# instanciation d'un objet de type "Bulle"
x = randint(0, ppx.width)
y = ppx.height
couleur = randint(0, 15)
rayon = randint(2, 10)
vitesse = randint(2, 10)
bulle = Bulle(x, y, couleur, rayon, vitesse) # arguments dans le même ordre que les paramètres du constructeur
ppx.run(update, draw)
- ligne 23 : un objet de type
Bulleest instancié; pour le désigner, on lui associe un nom de variable ( ici : bulle ). - lignes 11 : dans la fonction
update, on appelle alors la méthodemonterà partir de l'objet bulle; - ligne 15 : dans la fonction
draw, on appelle la méthodeafficherassociée à l'objet, en utilisant la syntaxe présentée dans le cours.
Des objets dans une liste ??
Dans le programme principal, on crée une liste dont chaque élément sera un objet :
bulles = [] # tableau d'objets de type 'Bulle'
for i in range(50): # 50 bulles
x = randint(0, ppx.width)
y = ppx.height
couleur = randint(1, 15)
rayon = randint(2, 5)
vitesse = randint(2, 5)
bulle = Bulle(x, y, couleur, rayon, vitesse)
bulles.append(bulle)
( on peut bien entendu créer le tableau bulles par compréhension.)
Dans les fonctions update et draw, on parcourt alors le tableau de ces objets, en appelant, pour chacun de ces éléments
( qui sont des objets de type Bulle ) leurs méthodes monter ou afficher :
def update():
for bulle in bulles: # parcours par valeur du tableau des bulles
bulle.monter()
if bulle.y < 0: # si la bulle arrive en haut de l'écran,
bulle.y = ppx.height # on réinitialise tous ses paramètres !
bulle.x = randint(0, ppx.width)
bulle.couleur = randint(1, 15)
bulle.taille = randint(2, 5)
bulle.vitesse = randint(2, 5)
def draw():
ppx.cls(0)
for bulle in bulles:
bulle.afficher()
Le code complet du script est ici.
Les balles rebondissantes
La méthode importante est la méthode rebonds : elle prend comme paramètre un autre objet de type Balle :
class Balle:
....
def rebonds(self, autre_balle): # le constructeur prend comme paramètre un AUTRE objet de type 'Balle'
dx = autre_balle.x - self.x
dy = autre_balle.y - self.y
distance = sqrt(dx**2 + dy**2)
minDist = autre_balle.taille + self.taille
if (distance < minDist):
self.vx = -self.vx
self.vy = -self.vy
autre_balle.vx = -autre_balle.vx
autre_balle.vy = -autre_balle.vy
Et cette méthode doit être appelée dans la fonction update ( pour une seule des balles ) :
def update():
# déplacement des balles
balle1.move()
balle2.move()
# gestion du rebond éventuel l'une sur l'autre
balle1.rebonds(balle2)
Le code complet du script est ici.
La fontaine colorée
Le code complet du script est ici.
Rebonds
Le code complet du script est ici.