Compare commits

..

4 Commits

Author SHA1 Message Date
popkex 2aebe0fef1 correction des collisions avec la balle (fait pas ia) 2026-06-29 13:46:15 +02:00
popkex a0d25c9768 - correction du déplacement de la balle + joueur (utilisation du dt)
-  modification todo
2026-06-29 13:34:52 +02:00
popkex 9d9c583598 - update todo
- ajout du mode difficile (fait a la main pour corriger par ia)
- correction du speedUp de la balle lors d'une collision
2026-06-28 12:51:37 +02:00
popkex 75b16dd16f repositionnement du bot 2026-06-27 14:14:47 +02:00
4 changed files with 126 additions and 39 deletions
+48 -15
View File
@@ -36,8 +36,8 @@ class Ball(Entity):
self.screen.blit(self.image, (self.pos.x, self.pos.y)) self.screen.blit(self.image, (self.pos.x, self.pos.y))
def speed_up(self): def speed_up(self):
self.velocity.x = self.velocity.x + .75 if (self.velocity.x > 0) else self.velocity.x - .25 self.velocity.x = self.velocity.x + .5 if (self.velocity.x > 0) else self.velocity.x - .5
self.velocity.y = self.velocity.y + .75 if (self.velocity.y > 0) else self.velocity.y - .25 self.velocity.y = self.velocity.y + .5 if (self.velocity.y > 0) else self.velocity.y - .5
def move(self, deltatime: float): def move(self, deltatime: float):
if (self.velocity.x == 0 and self.velocity.y == 0): # si la balle est a l'arret lui donner un mouvement aléatoire if (self.velocity.x == 0 and self.velocity.y == 0): # si la balle est a l'arret lui donner un mouvement aléatoire
@@ -55,26 +55,59 @@ class Ball(Entity):
self.velocity.x *= scale self.velocity.x *= scale
self.velocity.y *= scale self.velocity.y *= scale
if (self.pos.x < 0 or (self.pos.x + round(self.size / 2)) > self.screen.get_size()[0]): self.velocity.x = -self.velocity.x # Collision avec le mur # Réinitialise le flag de collision pour cette frame
self.collide = False
# collision avec le joueur # Collision avec les murs gauche/droite
if ((self.pos.y + self.size / 2) >= self.player.pos.y - self.player.size[1] and self.pos.y <= self.player.pos.y): if (self.pos.x < 0 or (self.pos.x + self.size) > self.screen.get_size()[0]):
if (self.pos.x + self.size >= self.player.pos.x and self.pos.x - self.size <= self.player.pos.x + self.player.size[0]): self.velocity.x = -self.velocity.x
# Récupère le centre et le rayon de la balle
ball_center_x = self.pos.x + self.size / 2
ball_center_y = self.pos.y + self.size / 2
ball_radius = self.size / 2
# Collision avec le joueur (raquette du bas)
player_left = self.player.pos.x
player_right = self.player.pos.x + self.player.size[0]
player_top = self.player.pos.y - self.player.size[1]
player_bottom = self.player.pos.y
# Vérifie si la balle entre en collision avec la raquette du joueur
if (ball_center_y + ball_radius >= player_top and
ball_center_y - ball_radius <= player_bottom and
ball_center_x + ball_radius >= player_left and
ball_center_x - ball_radius <= player_right):
# Vérifie que la balle vient de dessous (évite les faux positifs)
if self.velocity.y > 0:
self.velocity.y = -self.velocity.y self.velocity.y = -self.velocity.y
if not self.collide: self.speed_up() if not self.collide:
self.speed_up()
self.collide = True self.collide = True
# collision avec le bot # Collision avec le bot (raquette du haut)
elif (self.pos.y - round(self.size / 2) <= self.bot.pos.y and self.pos.y >= self.bot.pos.y + self.bot.size[1]): elif (ball_center_y > 0): # Évite les collisions négatives
if (self.pos.x + self.size >= self.bot.pos.x and self.pos.x - self.size <= self.bot.pos.x + self.bot.size[0]): bot_left = self.bot.pos.x
bot_right = self.bot.pos.x + self.bot.size[0]
bot_top = self.bot.pos.y
bot_bottom = self.bot.pos.y + self.bot.size[1]
# Vérifie si la balle entre en collision avec la raquette du bot
if (ball_center_y + ball_radius >= bot_top and
ball_center_y - ball_radius <= bot_bottom and
ball_center_x + ball_radius >= bot_left and
ball_center_x - ball_radius <= bot_right):
# Vérifie que la balle vient de dessus (évite les faux positifs)
if self.velocity.y < 0:
self.velocity.y = -self.velocity.y self.velocity.y = -self.velocity.y
if not self.collide: self.speed_up() if not self.collide:
self.speed_up()
self.collide = True self.collide = True
else: self.collide = False self.pos.x += self.velocity.x * deltatime * 100
self.pos.y += self.velocity.y * deltatime * 100
self.pos.x += round(self.velocity.x)
self.pos.y += round(self.velocity.y)
def as_winner(self) -> int: def as_winner(self) -> int:
if (self.pos.y < 0): return 0 # Joueur a gagner if (self.pos.y < 0): return 0 # Joueur a gagner
+3 -3
View File
@@ -17,7 +17,7 @@ class Game:
def restart(self): def restart(self):
self.running = True self.running = True
self.difficulty = 1 # 0: facile, 1: normal, 2: difficile, change uniquement l'intelligence du bot self.difficulty = 2 # 0: facile, 1: normal, 2: difficile, change uniquement l'intelligence du bot
self.player: Player = Player(self.screen_manager) self.player: Player = Player(self.screen_manager)
self.bot: Player = Player(self.screen_manager, difficulty=self.difficulty, is_ai=True) self.bot: Player = Player(self.screen_manager, difficulty=self.difficulty, is_ai=True)
@@ -36,9 +36,9 @@ class Game:
dt_ms: int = self.clock.tick(self.FPS_MAX) dt_ms: int = self.clock.tick(self.FPS_MAX)
dt: float = dt_ms / 1000.0 dt: float = dt_ms / 1000.0
self.player.move(dt, self.ball) self.player.move(dt, self.ball, self.player)
self.ball.move(dt) self.ball.move(dt)
self.bot.move(dt, self.ball) self.bot.move(dt, self.ball, self.player)
self.player.draw() self.player.draw()
self.bot.draw() self.bot.draw()
self.ball.draw() self.ball.draw()
-3
View File
@@ -1,8 +1,5 @@
""" """
TODO TODO
- améliorer l'ia
- rendre plus realiste les rebond
- modifier déplacement joueur & balle pour utiliser le deltatime
- faire un menu style arcade - faire un menu style arcade
- rendre le jeu plus arcade - rendre le jeu plus arcade
- ajouter des sons style arcade - ajouter des sons style arcade
+74 -17
View File
@@ -21,7 +21,7 @@ class Player(Entity):
image:pygame.Surface = pygame.Surface(self.size) image:pygame.Surface = pygame.Surface(self.size)
image.fill(color_player) image.fill(color_player)
origin: Tuple[int, int] = (round(screen_manager.get_screen().get_size()[0] / 2) - round(self.size[0] / 2), 105) if is_ai else (round(screen_manager.get_screen().get_size()[0] / 2) - round(self.size[0] / 2), 675) origin: Tuple[int, int] = (round(screen_manager.get_screen().get_size()[0] / 2) - round(self.size[0] / 2), 45) if is_ai else (round(screen_manager.get_screen().get_size()[0] / 2) - round(self.size[0] / 2), 675)
super().__init__("Player", origin, image, screen_manager) super().__init__("Player", origin, image, screen_manager)
@@ -40,8 +40,8 @@ class Player(Entity):
self.apply_friction(deltatime) self.apply_friction(deltatime)
temps_pos: Tuple[float, float] = ( temps_pos: Tuple[float, float] = (
self.pos.x + self.velocity.x, self.pos.x + self.velocity.x * deltatime * 100,
self.pos.y + self.velocity.y self.pos.y + self.velocity.y * deltatime * 100
) )
# les collisions # les collisions
@@ -55,7 +55,63 @@ class Player(Entity):
self.pos.x = temps_pos[0] self.pos.x = temps_pos[0]
self.pos.y = temps_pos[1] self.pos.y = temps_pos[1]
def ia_move(self, deltatime:float, ball:Ball): def _ball_center(self, ball_pos: Tuple[float, float], ball_size: float) -> Tuple[float, float]:
return ball_pos[0] + ball_size / 2, ball_pos[1] + ball_size / 2
def _mirror_x_center(self, x_center: float, radius: float) -> float:
min_x: float = radius
max_x: float = self.screen.get_width() - radius
span: float = max_x - min_x
if span <= 0:
return min_x
x_rel: float = x_center - min_x
x_mod: float = x_rel % (2 * span)
if x_mod <= span:
return min_x + x_mod
return max_x - (x_mod - span)
def predict_end_x(self, ball_pos:Tuple[float, float], ball_size:float, velocity_x:float, velocity_y:float, target_center_y:float) -> float:
center_x, center_y = self._ball_center(ball_pos, ball_size)
if velocity_y == 0:
return self._mirror_x_center(center_x, ball_size / 2)
dt: float = (target_center_y - center_y) / velocity_y
predicted_x: float = center_x + velocity_x * dt
return self._mirror_x_center(predicted_x, ball_size / 2)
def determinate_endx(self, ball_pos:Tuple[float, float], ball_size:float, dist_boty:float, coef_dir:float, modify_end_ballx: bool = True) -> float:
"""
return le delta x de déplacement de la balle
Determine ou se trouvera la balle en x quand elle sera au niveau du bot quand la balle monte vers le bot
"""
dtx: float = (dist_boty / coef_dir)
if (modify_end_ballx): self.end_ballx = ball_pos[0] + dtx # déterminer ou la ball devrais arriver
if (self.end_ballx < 0): # Si la balle devrait collisionner avec le mur de gauche
collisiony: float = ball_pos[1] + (coef_dir * (ball_pos[0] - (ball_size / 2))) # Calculer a quelle hauteur la balle va collisionner avec le mur
dist_boty = (self.pos.y - self.size[1]) - (collisiony - (ball_size / 2))
dtx = (dist_boty / -coef_dir)
if (modify_end_ballx): self.end_ballx = ball_pos[0] + dtx # déterminer ou la ball devrais arriver
return dtx
elif (self.end_ballx > self.screen.get_width() + (ball_size / 2)): # Si la balle devrait collisionner avec le mur de droite
collisiony: float = ball_pos[1] + (coef_dir * (self.screen.get_width() - (ball_pos[0] - (ball_size / 2)))) # Calculer a quelle hauteur la balle va collisionner avec le mur
dtx = (dist_boty / -coef_dir)
if (modify_end_ballx): self.end_ballx = ball_pos[0] + (dist_boty / -coef_dir) # déterminer ou la ball devrais arriver
return dtx
return dtx
def ia_move(self, deltatime:float, ball:Ball, player:Player):
"""
- mode facile :
le bot essaye juste de ce mettre ou se trouve la balle en temps reelle
- mode normale :
le bot anticipe ou se trouvera la balle quand elle sera a son niveau UNIQUEMENT quand elle vient vers lui
- mode difficile :
le bot anticipe ou se trouvera la balle quand elle sera a son niveau TOUT LE TEMPS
"""
# IA en mode facile # IA en mode facile
if (self.difficulty == 0): if (self.difficulty == 0):
if (ball.pos.x < self.pos.x + self.size[0] / 2): self.velocity.x -= self.speed * deltatime if (ball.pos.x < self.pos.x + self.size[0] / 2): self.velocity.x -= self.speed * deltatime
@@ -64,21 +120,22 @@ class Player(Entity):
# IA en mode normal && difficile # IA en mode normal && difficile
if (self.difficulty > 0): if (self.difficulty > 0):
if (ball.velocity.y < 0): # Si la balle va vers le bot (en gros, monte) if (ball.velocity.y < 0): # Si la balle va vers le bot (en gros, monte)
coef_dir: float = ball.velocity.y / ball.velocity.x # Calculer le coef directeur de la droite qui suit le vecteur mouvement de la balle target_center_y: float = self.pos.y + self.size[1] + (ball.size / 2)
dist_boty: float = (self.pos.y - self.size[1]) - (ball.pos.y - (ball.size / 2)) # La distance en yqui séparre le bot et la balle self.end_ballx = self.predict_end_x(ball_pos=(ball.pos.x, ball.pos.y), ball_size=ball.size, velocity_x=ball.velocity.x, velocity_y=ball.velocity.y, target_center_y=target_center_y)
self.end_ballx = ball.pos.x + (dist_boty / coef_dir) # déterminer ou la ball devrais arriver # else: self.end_ballx = round(self.screen.get_size()[0] / 2)
if (self.end_ballx < 0): # Si la balle devrait collisionner avec le mur de gauche
collisiony: float = ball.pos.y + (coef_dir * (ball.pos.x - (ball.size / 2))) # Calculer a quelle hauteur la balle va collisionner avec le mur
dist_boty = (self.pos.y - self.size[1]) - (collisiony - (ball.size / 2))
self.end_ballx = ball.pos.x + (dist_boty / -coef_dir) # déterminer ou la ball devrais arriver
elif (self.end_ballx > self.screen.get_width() + (ball.size / 2)): # Si la balle devrait collisionner avec le mur de droite
collisiony: float = ball.pos.y + (coef_dir * (self.screen.get_width() - (ball.pos.x - (ball.size / 2)))) # Calculer a quelle hauteur la balle va collisionner avec le mur
self.end_ballx = ball.pos.x + (dist_boty / -coef_dir) # déterminer ou la ball devrais arriver
if (self.end_ballx < self.pos.x + self.size[0] / 2): self.velocity.x -= self.speed * deltatime if (self.end_ballx < self.pos.x + self.size[0] / 2): self.velocity.x -= self.speed * deltatime
elif (self.end_ballx > self.pos.x + self.size[0] / 2): self.velocity.x += self.speed * deltatime elif (self.end_ballx > self.pos.x + self.size[0] / 2): self.velocity.x += self.speed * deltatime
if (self.difficulty == 2): # Le mode difficile
if (ball.velocity.y > 0): # Quand la balle se dirige vers le joueur
player_contact_center_y: float = player.pos.y - (ball.size / 2)
impact_center_x: float = self.predict_end_x(ball_pos=(ball.pos.x, ball.pos.y), ball_size=ball.size, velocity_x=ball.velocity.x, velocity_y=ball.velocity.y, target_center_y=player_contact_center_y)
target_center_y: float = self.pos.y + self.size[1] + (ball.size / 2)
impact_top_left: Tuple[float, float] = (impact_center_x - (ball.size / 2), player_contact_center_y - (ball.size / 2))
self.end_ballx = self.predict_end_x(ball_pos=impact_top_left, ball_size=ball.size, velocity_x=ball.velocity.x, velocity_y=-ball.velocity.y, target_center_y=target_center_y)
self.apply_move(deltatime) self.apply_move(deltatime)
@@ -97,8 +154,8 @@ class Player(Entity):
self.apply_move(deltatime) self.apply_move(deltatime)
def move(self, deltatime: float, ball: Ball): def move(self, deltatime: float, ball: Ball, player:Player):
if (self.is_ai): if (self.is_ai):
self.ia_move(deltatime, ball) self.ia_move(deltatime, ball, player)
else: else:
self.player_move(deltatime) self.player_move(deltatime)