1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
| """
Interface Homme-machine (ihm) basée sur pyxel et le modèle MVC
On découpe les fonctions en 3 catégories :
- celles du (M)ODELE : la gestion des données pures
- celles de la (V)UE : la gestion de l'interaction avec l'utilisateur
- celles du (C)ONTROLEUR : la logique du programme, notamment liaison vue-modèle
La documentation utilise le mot "Adresse" pour désigner la référence d'un objet.
Plus simple et explicite, surtout en CPython.
Mais vous pouvez le remplacer par référence si vous voulez être rigoureux.
"""
# 1 - Importation(s) ==========================================================
import pyxel
import random
# 2 - Constantes ==============================================================
LRG = 128
HTR = 128
# 3 - Déclarations des classes ================================================
class Jeu:
def __init__(self):
self.DIMX = LRG # Largeur constante de l'écran
self.DIMY = HTR # Hauteur constante de l'écran
self.j1 = Joueur(1, self) # Adresse d'une instance de Joueur
self.j2 = Joueur(2, self) # Adresse d'une instance de joueur
self.balle = Balle(6, self) # Adresse d'une instance de Balle
class Joueur:
def __init__(self, n:int, jeu:Jeu):
self.n = n # numéro du joueur
self.pts = 0 # points du joueur
self.jeu = jeu # Adresse de l'instance de Jeu auquel appartient ce joueur
self.raq = None # Adresse de l'instance de Raquette de ce joueur
# Instanciation de la raquette
if n == 1:
self.raq = Raquette(10, 40, 2, self)
elif n == 2:
self.raq = Raquette(110, 40, 3, self)
class Raquette:
def __init__(self, x:int, y:int, couleur:int, joueur:Joueur):
self.j = joueur # Adresse de l'instance de Joueur qui possède cette raquette
self.x = x # coordonnée x du coin en haut à gauche
self.y = y # coordonnée y du coin en haut à gauche
self.couleur = couleur # int, couleur du vaisseau dans Pyxel
self.hauteur = self.j.jeu.DIMY // 5 # hauteur en pixel
self.epaisseur = self.j.jeu.DIMX // 16 # épaisseur en pixel
class Balle:
def __init__(self, couleur:int, jeu:Jeu):
self.jeu = jeu # Adresse de l'instance de Jeu où apparaît cette balle
self.x = jeu.DIMX / 2 # Float, coordonnée horizontale du centre
self.y = jeu.DIMY / 2 # Float, coordonnée verticale du centre
self.rayon = jeu.DIMX // 50 # Rayon du cercle en pixel
self.couleur = couleur # Int, couleur du cercle
self.dx = random.randint(-10, 10) / 10 # Float, variation horizontale en px chaque 1/30e s
self.dy = random.randint(-5, 5) / 10 # Float, variation verticale en px chaque 1/30e s
# 4 - Déclarations des fonctions ==============================================
def controle() -> None: # CONTROLEUR
"""Fonction qui récupère les événements et modifie les données en conséquence"""
if pyxel.btn(pyxel.KEY_A): # Si on a appuyé sur A
mvt_raq(jeu.j1.raq, 0, -1) # on modifie les données de la raquette 1
if pyxel.btn(pyxel.KEY_Q): # Si on a appuyé sur Q
mvt_raq(jeu.j1.raq, 0, 1) # on modifie les données de la raquette 1
if pyxel.btn(pyxel.KEY_UP): # Si on a appuyé sur haut
mvt_raq(jeu.j2.raq, 0, -1) # on modifie les données de la raquette 2
if pyxel.btn(pyxel.KEY_DOWN): # Si on a appuyé sur bas
mvt_raq(jeu.j2.raq, 0, 1) # on modifie les données de la raquette 2
mvt_balle(jeu.balle) # on modifie les données de la balle
def mvt_raq(r:Raquette, dx:int, dy:int) -> None: # MODELE
"""Modifie les données de l'objet pour intégrer le déplacement dx et dy voulu"""
r.x = gerer_blocage(r.x, dx, r.j.jeu.DIMX)
r.y = gerer_teleportation(r.y, dy, r.j.jeu.DIMY)
def mvt_balle(b:Balle) -> None: # MODELE
"""Modifie les données de la balle pour intégrer le nouveau déplacement"""
# calcul des nouvelles coordonnées
b.x = b.x + b.dx
b.y = b.y + b.dy
# gestion des collisions éventuelles avec les raquettes
c = collision_avec(b)
if c == 1:
r1 = b.jeu.j1.raq
b.x = r1.x + r1.epaisseur + b.rayon + 1
b.dx = -b.dx
if c == 2:
r2 = b.jeu.j2.raq
b.x = r2.x - b.rayon - 1
b.dx = -b.dx
# gestion des collisions éventuelles avec plafond et sol
if b.y > b.jeu.DIMY:
b.y = b.jeu.DIMY - 1
b.dy = -b.dy
elif b.y < 1:
b.y = 1
b.dy = -b.dy
def gerer_teleportation(ancienne:int, deplacement:int, maximum:int) -> int:
"""Calcule la nouvelle coordonnée de l'objet en passant d'un côté à l'autre"""
nouvelle = ancienne + deplacement
if nouvelle > maximum:
nouvelle = 1
elif nouvelle < 1:
nouvelle = maximum - 1
return nouvelle
def gerer_blocage(ancienne:int, deplacement:int, maximum:int) -> int:
"""Calcule la nouvelle coordonnée de l'objet en considérant des cloisons limites"""
nouvelle = ancienne + deplacement
if nouvelle > maximum:
nouvelle = maximum - 1
elif nouvelle < 1:
nouvelle = 1
return nouvelle
def collision_avec(b:Balle) -> int:
"""Renvoie le numéro de la raquette 1 ou 2 touchant la balle, 0 si aucune"""
# Récupération des adresses des deux raquettes
r1 = b.jeu.j1.raq
r2 = b.jeu.j2.raq
# Si la balle possède un x potentiellement au contact de la raquette 1
if (b.x - b.rayon >= r1.x + r1.epaisseur//2) and (b.x - b.rayon <= r1.x + r1.epaisseur):
# Si la balle est également verticalement dans la zone de la raquette 1
if (b.y + b.rayon >= r1.y) and (b.y - b.rayon <= r1.y + r1.hauteur):
# C'est que la balle est en contact avec la raquette du joueur 1
return 1
# Si la balle possède un x potentiellement au contact de la raquette 2
if (b.x + b.rayon >= r2.x) and (b.x - b.rayon <= r2.x + r2.epaisseur//2):
# Si la balle est également verticalement dans la zone de la raquette 2
if (b.y + b.rayon >= r2.y) and (b.y - b.rayon <= r2.y + r2.hauteur):
# C'est que la balle est en contact avec la raquette du joueur 2
return 2
# Si on arrive ici, c'est que la balle n'est au contact d'aucune raquette
return 0
def actualiser_vue() -> None: # VUE
"""destruction et création du visuel (30 fois par seconde)"""
pyxel.cls(0) # efface la fenetre
afficher_raquette(jeu.j1.raq) # raquette 1
afficher_raquette(jeu.j2.raq) # raquette 2
afficher_balle(jeu.balle)
def afficher_raquette(r:Raquette) -> None: # VUE
"""Dessine une raquette"""
pyxel.rect(r.x, r.y, r.epaisseur, r.hauteur, r.couleur) # (x,y) est en haut à gauche
def afficher_balle(b:Balle) -> None: # VUE
"""Dessine une balle"""
pyxel.circ(int(b.x), int(b.y), b.rayon, b.couleur) # (x,y) est le centre
# 5 - PROGRAMME PRINCIPAL========= ================================================
if __name__ == "__main__":
jeu = Jeu()
pyxel.init(LRG, HTR, title="Mon jeu") # Démarrage de la fenêtre graphique
pyxel.run(controle, actualiser_vue) # Alternance controle/vue 30 fois par seconde
|