print("Bonjour, je vais bientĂŽt programmer le jeu Pong !!!")
print()
?
Comment sont représentées les couleurs numériquement ?
Positionnement dans une fenĂȘtre d'affichage.
Découvrir la documentation de Pygame...
cible.py
:
pygame.draw.circle()
pour tracer un cercle, ou encore pygame.display.flip()
. Il est possible de raccourcir ces écritures :
L'utilisation de librairies en Python...
pygame.display.quit()
sys.exit()
provenant du module sys
delay()
de la librairie time
.
Les différents types de variables que l'on peut rencontrer en programmation.
Attributs de la balle | Nom de la variable | Type de la variable |
---|---|---|
Couleur | c | Liste de 3 entiers int |
Rayon | r | int |
Position (abscisse) | x | int |
Position (ordonnée) | y | int |
Déplacement (abscisse) | dx | int |
Déplacement (ordonnée) | dy | int |
Forme | cercle (pour l'instant cette variable ne changera pas) | --- |
Actuellement, l'image est tracĂ©e une seule fois. Il va falloir utiliser une boucle infinie modifiant le contenu de la fenĂȘtre d'affichage 30 fois par seconde. CelĂ signifie que chaque itĂ©ration de la boucle doit durer $\frac{1}{30}$-Ăšme de seconde.
Cette boucle de jeu sera créee à l'aide d'une boucle "While" qui vous est présentée ici :
Présentation de la boucle while permettant de répéter des instructions de maniÚre répétée...
dx
suivant lâaxe x et dy
suivant lâaxe y. Il faut donc rajouter dans la boucle infinie les instructions suivantes :
Normalement, le résultat est le suivant :
screen.fill(couleurFond)
x = x+dx
et y=y+dy
. Ils se résument dans le tableau suivant :
Attribution Ă $x$ d'une valeur $a$ | Ajouter $a$ Ă une variable $x$ | Retirer $a$ Ă une variable $x$ | Multiplier une variable $x$ par $a$ | Diviser une variable $x$ par $a$ |
---|---|---|---|---|
x=a |
x+=a |
x-=a |
x*=a |
x/=a |
dx
est positifdx
est négatif
dx = dx * (-1)
|
dx *= (-1)
|
dx = -dx
|
On considÚre que la balle entre en collision avec le bord lorsque la condition suivante est vérifiée : $$x+r >width$$
On considÚre que la balle entre en collision avec le bord lorsque la condition suivante est vérifiée : $$(x-r \lt 0)$$
code source : pong1.3.py
Si l'une des conditionx+r > width
OU x-r < 0
est remplie, elle rĂ©alisera la mĂȘme instruction dx = -dx
.
On peut regrouper ces deux conditions en un seul test :
supérieur | inférieur | supérieur ou égal | inférieur ou égal | égalité |
---|---|---|---|---|
> |
< |
>= |
<= |
== |
addition | soustraction | multiplication | division | modulo (reste de la division euclidienne) |
---|---|---|---|---|
+ |
- |
* |
/ |
% |
Attribution Ă $x$ d'une valeur $a$ | Ajouter $a$ Ă une variable $x$ | Retirer $a$ Ă une variable $x$ | Multiplier une variable $x$ par $a$ | Diviser une variable $x$ par $a$ |
---|---|---|---|---|
x=a |
x+=a |
x-=a |
x*=a |
x/=a |
On souhaite maintenant ajouter des balles au jeu. Chaque balle doit avoir 5 attributs (positions [x,y], vitesses [dx, dy], rayon r). Actuellement, Si on veut afficher 10 balles, cela revient à manipuler 5*10 = 50 variables différentes.
De mĂȘme, chaque balle doit avoir des actions propres (afficher, avancer, test de collision) qu'il faudrait dupliquer.
Une mĂ©thode grossiĂšre consiste Ă copier chaque partie du code rĂ©alisĂ© pour la premiĂšre balle et Ă lâadapter pour chaque balle. Cela conduirait Ă un code trĂšs long, rĂ©pĂ©titif et fastidieux Ă lire.
Une des solutions pour remĂ©dier Ă ce problĂšme est :Il suffit pour cela dâincorporer les diffĂ©rentes lignes de codes Ă©crites pour chaque actions dans la boucle while dans des fonctions indĂ©pendantes qui peuvent ĂȘtre appelĂ©es Ă nâimporte quel instant.
De maniĂšre gĂ©nĂ©rale, une fonction peut avoir des paramĂštres en entrĂ©e et renvoyer des paramĂštres en sortie. Dans le cas prĂ©sent, dans le cas dâune balle unique, on pourra dĂ©finir les fonctions suivantes :afficher()
: permet de dessiner la balleavancer()
: permet de faire avancer la balletestCollision()
: teste la collision sur les bordsfill(color)
: donne une couleur color au fondcircle(screen, couleurBalle , [x,y], r, 0)
: dessine un cercle sur l'écran à une position donnée, une couleur et un rayon.afficher()
, nous procédons de la maniÚre suivante :
afficher()
avec le mot clé def
C'est normal ! CelĂ signifie que la variable x
est inconnue dans la fonction afficher()
.
En effet, les variables utilisĂ©es dans chaque fonction ont par dĂ©faut une portĂ©e locale. CelĂ signifie qu'elle doivent ĂȘtre dĂ©clarĂ©e et utilisĂ©e Ă l'intĂ©rieur de la fonction. Elle n'existent pas en dehors de cette fonction.
Si une variable, dĂ©clarĂ©e a l'extĂ©rieur, doit ĂȘtre utilisĂ©e et modifiĂ©e Ă l'intĂ©rieur de la fonction il est nĂ©cessaire de la dĂ©clarĂ©e comme variable globale en dĂ©but de fonction comme ceci :
global x,y,dx,dy
testEvenement()
qui s'occupe de la dĂ©tection des Ă©vĂšnements clavier/souris et qui gĂšre l'arrĂȘt du jeu et la fermeture de la fenĂȘtre.
La solution est de dĂ©finir une liste de valeurs pour chaque attribut de la balle. Si nous manipulons N balles, chaque liste comportera N valeurs de mĂȘme type. Chaque liste est alors considĂ©rĂ© comme une variable unique qui permet de manipuler N valeurs.
Quel que soit le nombre N de balles, lâutilisation des listes permet alors de ne manipuler que 6 variables correspondant Ă chaque attribut dâune balle.
Pour découvrir les listes, nous allons utiliser la console de l'IDE :
L
de 3 valeurs (qui pourrait représenter une couleur RVB)
L
étant toujours en mémoire dans la console) :
[]
. Taper la commande suivante :
20
car le premier élément de la liste est repéré par l'indice 0
L
Elle vaut maintenant ['pong is the best game!!', 10, 15]
.
L
contient une chaĂźne de caractĂšre et deux entiers.
Commande | Commentaire |
---|---|
len(L) |
La fonction len renvoie la longueur de la liste L |
L.append(val) |
ajouter une valeur val aprÚs le dernier élément de la liste |
L.insert(i,val) |
ajouter une valeur val aprÚs le i-Úme élément de la liste |
L+=L0 |
concaténer la liste L0 à la liste L |
del(L[i]) |
retirer le i-Úme élément de la liste L |
a=L.pop() |
retirer le dernier élément de la liste L et le stocke dans a . |
L.remove(val) |
retirer la premiĂšre occurence d'un Ă©lĂ©ment ayant la mĂȘme valeur que val . |
L.reverse() |
renverse l'ordre |
L.sort() |
réordonne les éléments de la liste |
val in L |
renvoie True si un élément de la liste vaut val , False sinon. |
val not in L |
renvoie False si un élément de la liste vaut val , True sinon. |
L
42
Ă la fin de la liste
3.141592
en 2Ăšme position de la liste (attention le "premier" est L[0]
)
>>> len(L)
>>> L.append(42)
>>> L.insert(1,3.141592)
>>> del(L[2])
ou L.remove(3.141592)
dx
: listes des déplacements horizontaux entre deux framesdy
: liste des déplacement verticaux entre deux framesx
: liste des abscisses des positionsy
: liste des ordonnées des positionsr
: liste des rayons des ballesi
de la balle sur laquelle elle s'applique. On ajoute un argument i
en entrée de la fonction :
append
vue précédemment dx
, dy
, x
, y
et r
.
random
randint()
de la librairie random
. On l'importe de la maniĂšre suivante :
valMin
et valMax
(compris), on utilise :
dx
, dy
, x
, y
, r
ET couleurBalle
for
qui permet lâexĂ©cution dâun bloc dâinstructions un certain nombre de fois, en faisant gĂ©nĂ©ralement varier un indice entre une valeur initiale et une valeur finale avec un pas dâincrĂ©mentation
i
varie d'une valeur initiale i=0
Ă une valeur finale i=4
et non 5 !!.
range()
list(range(0,5))
, on obtient :
range
.
list(range(2,5))
list(range(0,100,10))
list(range(10,0,-1))
N
qui représente le nombre de balles. Modifier le code précédent pour afficher N balles. Testez différentes valeurs.
rx = 20
: abscisse de la raquette ry = height/2
: ordonnée de la raquette (positionnée au milieu initialement mais qui variera)dry = 5
: déplacement vertical de la raquetterh = 100
: hauteur de la raquette rw = 20
: largeur de la raquette rcolor = [255, 255, 255]
: couleur de la raquette (ici blanc) pygame.draw.rect()
qui s'utilise de la maniĂšre suivante :
afficher_raquette()
:
KEYDOWN
ou QUIT
ou K_UP
, etc. au lieu de pygame.KEYDOWN
ou pygame.QUIT
ou pygame.K_UP
, etc.
testEvenements()
qui gĂšre l'Ă©vĂšnement "quitter la fenĂȘtre de jeu".
Si l'évÚnement vaut pygame.KEYDOWN
, c'est que une touche (n'importe laquelle) a été appuyée.
testEvenement()
comme ceci :
pygame.key.get_pressed()
renvoie une liste représentant l'état de chaque touche du clavier : 1 pour appuyée, 0 pour relùchée.
pygame.key.get_pressed()
est 273, celui de "flĂšche du bas" est 274
K_UP
vaut 273 et K_DOWN
vaut 274.
bouger_raquette()
suivante :
bouge_raquette()
afin de réaliser le déplacement souhaité.
KEYDOWN
est détecté
testCollision(i)
qui teste la collision de la ballei
avec les bords. Il est nécessaire de faire un schéma pour bien comprendre la situation :
and
. Pour la i-Úme balle, cette condition logique s'écrit donc :
testCollision
pour que le bord droit de la raquette se comporte comporte comme le bord droit de l'écran et fasse rebondir la balle.
Le pong réalisé est ainsi jouable. Il ne s'agit bien sûr pas d'un jeu finalisé. Il nous aura permis d'introduire toutes les notions fondamentales de programmation python ainsi que des morceaux de code réutilisables pour d'autres types de projets.
Il est possible de poursuivre et d'améliorer le jeu :
pygame.Rect
est un outil que nous n'avons pas utilisé mais qui est plus adapté aux problÚmes de collision.