Ce document a pour but de présenter une méthode rapide, ludique et efficace pour faire comprendre et manipuler les notions de base de la programmation web par le codage du jeu « Pong » en HTML/CSS/JavaScript.
Suivre la construction pas Ă pas d'un jeu comme Pong devrait permettre de comprendre et manipuler les notions de base de la programmation web HTML/CSS/JavaScript.
La structure HTML de la page et les propriétés CSS des élément seront manipulée à l'aide de JavaScript afin de créer le jeu qui sera en fait une page web dynamique et interactive.
On (re?)découvrira les notions programmation de base que sont les variables, tests, boucles, objets, etc.
Comment sont représentées les couleurs numériquement ?
Positionnement dans une fenĂȘtre d'affichage.
En se basant sur les éléments de cours précédent positionnement CSS, on souhaite réaliser le premier objectif d'affichage de la balle dans un cadre.
<link rel="stylesheet" href="pong.css">
permet de lier les deux fichiers html et css entre eux.
div
d'id zone-jeu
.
#zone-jeu
.
div
de classe ball
.ball
.
border-radius
permet de préciser le rayon des coins arrondis.
.ball{border-radius:50%;}
cible.py
:
int
: nombre entier float
: nombre flottant (Ă virgule)string
: chaĂźne de caractĂšresbool
: booléen - valeur de vérité vrai ou fauxv
en tapant type(v)
. Vérifiez dans la console Python les types de :
42
"salut"
1.5
math.pi
True
"True"
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) | --- |
Normalement, le résultat est le suivant :
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 |
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 |
Supposons quâon souhaite maintenant faire Ă©voluer plusieurs balles dans la fenĂȘtre dâaffichage. Chaque balle doit avoir un ensemble dâattributs et de variables associĂ©es qui lui est propre. Actuellement, chaque balle dispose de 6 attributs qui la caractĂ©risent de maniĂšre unique. Si on dĂ©cide de faire Ă©voluer 10 balles, cela revient Ă manipuler 6*10 = 60 variables diffĂ©rentes. De mĂȘme, chaque balle doit avoir des actions propres (afficher, avancer, test de collision). Il faudrait donc dupliquer ces actions pour chaque balle.
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.