import io
with redirect(stdout=io.StringIO()):
import tdoc.pygame
setup_canvas()
import pygame
import math
import pathlib
from random import randint, choice
width, height = 600, 600
window = pygame.display.set_mode((width, height))
pygame.init()
async def refresh(color):
await animation_frame()
pygame.display.flip()
window.fill(color)
class Actor(pygame.sprite.Sprite):
def __init__(self, image_path, cx, cy):
super().__init__()
self.image_path = image_path
self.original_image = pygame.image.load(image_path).convert_alpha()
self.image = self.original_image.copy()
self.rect = self.image.get_rect(center=(cx, cy))
self.collision_margin = 0.05 # 5% de marge
self.angle = 0
@property
def collision_rect(self):
"""Crée le rect de collision à la volée basé sur le rect actuel"""
return self.rect.inflate(
-self.rect.width * self.collision_margin,
-self.rect.height * self.collision_margin
)
def draw(self):
global window
window.blit(self.image, self.rect)
def get_x(self):
return self.rect.centerx
def get_y(self):
return self.rect.centery
def move(self, dx, dy):
self.rect.move_ip(dx, dy)
def set_position(self, x, y):
self.rect.centerx = x
self.rect.centery = y
def collide(self, other_actor):
other_rect = getattr(other_actor, 'collision_rect', other_actor.rect)
return self.collision_rect.colliderect(other_rect)
def flip(self, horizontal=True, vertical=False):
self.image = pygame.transform.flip(self.image, horizontal, vertical)
self.rect = self.image.get_rect(center=self.rect.center)
def scale(self, factor):
new_size = (
int(self.original_image.get_width() * factor),
int(self.original_image.get_height() * factor)
)
self.image = pygame.transform.scale(self.original_image, new_size)
self.rect = self.image.get_rect(center=self.rect.center)
def kill(self):
self.image = pygame.Surface((0, 0))
self.original_image = pygame.Surface((0, 0))
self.rect = self.image.get_rect()
def set_angle(self, angle):
self.angle = angle
self.image = pygame.transform.rotate(self.original_image, angle)
class Text(pygame.sprite.Sprite):
def __init__(self, text, cx, cy, *args):
super().__init__()
self.font = pygame.font.Font(None, 36)
self.image = self.font.render(text, *args)
self.rect = self.image.get_rect()
self.rect.centerx, self.rect.centery = cx, cy
def draw(self):
global window
window.blit(self.image, self.rect)
def kill(self):
self.image = pygame.Surface((0,0))
self.rect = self.image.get_rect()
class Timer(pygame.time.Clock):
def __init__(self, time):
super().__init__()
self.time = time * 1000
self.started = False
def start(self):
self.started = True
self.start_time = animation_time()
self.end_time = self.start_time + self.time
def is_finished(self):
#A retravailler
if not self.started:
return True
return animation_time() >= self.end_time
def __str__(self):
if not self.started:
return "Not started"
elif self.is_finished():
return "0.00s"
else:
return f"{(self.end_time - animation_time()) / 1000:.2f}s"
def get_pressed_keys():
pressed_list = []
keys = pygame.key.get_pressed()
# 1. La boucle pour les touches "standards" (lettres, chiffres)
# On itère sur les scancodes standards
for scancode in range(len(keys)):
if keys[scancode]:
try:
name = pygame.key.name(scancode).upper()
# On filtre un peu pour éviter d'avoir des trucs bizarres ou vides
if name and name != "UNKNOWN":
pressed_list.append(name)
except ValueError:
pass
# 2. Vérification MANUELLE des touches spéciales (Flèches, Ctrl, etc.)
# C'est nécessaire car elles échappent souvent à la boucle simple ci-dessus
if keys[pygame.K_UP]:
pressed_list.append("UP")
if keys[pygame.K_DOWN]:
pressed_list.append("DOWN")
if keys[pygame.K_LEFT]:
pressed_list.append("LEFT")
if keys[pygame.K_RIGHT]:
pressed_list.append("RIGHT")
# Tu peux aussi ajouter d'autres touches spéciales souvent manquées par la boucle :
if keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]:
pressed_list.append("SHIFT")
if keys[pygame.K_SPACE]:
# Parfois "SPACE" ressort comme " " (vide) dans le name(), donc utile de le forcer
if " " in pressed_list: pressed_list.remove(" ") # Nettoyage optionnel
if "SPACE" not in pressed_list: pressed_list.append("SPACE")
# 3. Souris (Ton code original)
mouse_buttons = pygame.mouse.get_pressed()
if mouse_buttons[0]: pressed_list.append("MOUSE_1")
if mouse_buttons[1]: pressed_list.append("MOUSE_2")
if mouse_buttons[2]: pressed_list.append("MOUSE_3")
return pressed_list
try:
await main()
finally:
pygame.quit()
Projet Pacman-like#
Partie 1 : fonctionnalités de base#
Créez et dessinez l'acteur principal pacman au centre de l'écran avec l'image pacman.png. Rappel : l'écran fait 600x600 pixels.
Dans ce jeu, pacman doit se déplacer automatiquement. Appuyer sur une touche permet de changer la direction de ce déplacement automatique.
Avant la boucle de jeu, créez deux variables
vitesse_x = 0etvitesse_y = -3pour contrôler la direction du mouvement.Dans la boucle de jeu, déplacez en tout temps pacman avec
move()par rapport à ses deux vitesses.
pacman devrait maintenant avancer continuellement vers le haut et sortir de l'écran.
Permettez à pacman de changer de direction avec les touches WASD.
Dans la boucle de jeu, récupérez les touches pressées avec
get_pressed_keys().Si la touche A est pressée, changez la direction vers la gauche. Cela signifie que
vitesse_x = -3etvitesse_y = 0Si la touche S est pressée, changez la direction vers le bas :
vitesse_x = 0etvitesse_y = 3Faites de même pour pour les touches W et D.
Vous pouvez maintenant contrôler la direction de pacman qui avance continuellement !
Faisons en sorte que pacman regarde dans la direction où il se déplace.
Juste après avoir changé les vitesses avec les touches WASD, utilisez la méthode
set_angle()pour orienter pacman :Si vous allez vers la gauche, l'angle est de 90° :
pacman.set_angle(90)Si vous allez vers le bas, l'angle est de 180°
Vers la droite, l'angle est de 270°
Vers le haut, l'angle est de 0°
pacman devrait maintenant tourner pour regarder dans la direction où il se déplace !
pacman doit rebondir sur les bords de l'écran.
Si la position horizontale de pacman (
pacman.get_x()) est inférieure à 0 OU supérieure à 600, inversez la vitesse horizontale :vitesse_x = -vitesse_xFaites de même avec la position verticale (
get_y()etvitesse_y).
pacman devrait maintenant rebondir sur les murs comme une balle de ping-pong !
Faisons apparaître des pommes à ramasser aléatoirement.
Avant la boucle de jeu, créez une liste vide
pommes = [].Créez également un
timer_pommes = Timer(2)et démarrez-le immédiatement avectimer_pommes.start().Dans la boucle de jeu, vérifiez si le timer est terminé (
if timer_pommes.is_finished():).Si c'est le cas :
Créez un nouvel acteur
pommeavec l'imageapple.png. Pour ses positions x et y, à la place de valeurs fixes, utilisez à chaque foisrandint(30, 570). Cette fonction permet de tirer un nombre entier aléatoire entre ces deux bornes et donc de placer la pomme aléatoirement sur l'écran.Ajoutez cette pomme à la liste
pommes.Redémarrez le timer.
Utilisez une boucle
fordans la boucle de jeu pour dessinerdraw()toutes les pommes. Pour chaque pomme dans la liste de pommes : draw cette pomme.
Des pommes devraient apparaître toutes les 2 secondes à des positions aléatoires.
pacman doit pouvoir ramasser les pommes.
Avant la boucle de jeu, créez une variable
score = 0.Créez également un texte pour afficher le score :
txt_score = Text("Score: 0", 70, 30, True, (255, 255, 255), (0, 0, 0)).Dans la boucle de jeu, parcourez la liste des pommes avec une boucle
for.Pour chaque pomme, vérifiez si pacman est en collision avec elle :
if pacman.collide(pomme):.Si c'est le cas :
Supprimez la pomme de la liste avec
remove().Augmentez le score de 10 :
score += 10.Mettez à jour le texte :
txt_score = Text(f"Score: {score}", 70, 30, True, (255, 255, 255), (0, 0, 0)).
N'oubliez pas de dessiner
draw()le texte du score dans la boucle de jeu.
Les pommes devraient maintenant disparaître quand vous les touchez et le score augmenter !
Créons maintenant un fantôme qui se déplace tout seul.
Avant la boucle de jeu, créez un acteur
fantômeavec l'imageblinky.pngà une position aléatoire.Créez deux variables
vitesse_fantome_x = 2etvitesse_fantome_y = 2pour contrôler le mouvement du fantôme.Dans la boucle de jeu, déplacez le fantôme avec
move()de manière similaire à pacman.N'oubliez pas de dessiner
draw()le fantôme.
Le fantôme devrait maintenant se déplacer en diagonale et sortir de l'écran.
Le fantôme doit rebondir sur les bords de l'écran, exactement comme pacman.
Si la position horizontale du fantôme est inférieure à 0 OU supérieure à 600, inversez-la.
Faites de même avec la position verticale.
Le fantôme devrait maintenant rebondir sur les murs et se déplacer partout dans l'écran !
Ajoutons la condition de gameover quand pacman touche le fantôme.
Dans la boucle de jeu, vérifiez si pacman est en collision avec le fantôme avec
collide(). Si c'est le cas, utilisez la fonctionkill()pour détruire pacman
pacman devrait maintenant disparaître lorsqu'il entre en contact avec le fantôme.
async def main():
while True:
await refresh((0, 0, 0))
Partie 2 : fonctionnalités intermédiaires#
Les fonctionnalités de la partie 2 valent au maximum 10 pts. Vous pouvez donc choisir des éléments de la liste suivante à implémenter et s'additionnant à 10 pts.
(2 pts) Faites accélérer progressivement le fantôme. À chaque fois que pacman ramasse une pomme, augmentez légèrement les vitesses du fantôme (en conservant leur signe) pour rendre le jeu plus difficile.
(4 pts) Ajoutez un système de vies. pacman commence avec 3 vies affichées à l'écran. Toucher un fantôme fait perdre une vie au lieu du gameover immédiat. Après avoir perdu une vie, pacman est invincible pendant 2 secondes. Le gameover n'arrive que lorsque toutes les vies sont perdues. +1 pt si vous utilisez des images de coeur (
heart.png).(2 pts) Ajoutez de nouveaux fruits autres que la pomme qui apparaissent aléatoirement. Ceux-ci permettent d'obtenir plus de points que la pomme. Vous pouvez utiliser
banana.png,orange.pngoustrawberry.png.(4 pts) Ajoutez de nouveaux fruits autres que la pomme qui apparaissent aléatoirement. Ceux-ci permettent d'obtenir des bonus tels que pacman qui bouge plus rapidement, le fantôme qui ralentit, etc.
(3 pts) Ajoutez un 2ème fantôme (
inky.png) qui apparaît lorsque on atteint un certain nombre de points(2 pts) Ajoutez un "écran" de gameover.
Partie 3 : fonctionnalités avancées#
(2 pts) Afin que le jeu ne commence pas immédiatement lorsqu'on lance le programme, définissez une touche sur laquelle on doit appuyer afin que le jeu démarre.
(3 pts) Ajoutez une fonctionnalité permettant de redémarrer le jeu une fois qu'on a perdu et qu'on appuie sur la touche
R.(2 pts) Rendez le mouvement du fantôme moins prévisible d'une manière ou d'une autre. Par exemple, à interval régulier, celui-ci pourrait aléatoirement changer de directement. Ou, lorsqu'il rebondit contre un mur, il pourrait aléatoirement revenir dans la même direction.
(3 pts) Ajoutez un obstacle fixe sur le terrain. Lorsque Pacman entre en collision avec cet obstacle, il rebondit comme sur un mur. +2pts si vous utilisez une liste d'obstacles.
(5 pts) Ajoutez un 2ème niveau avec une condition de victoire. Lorsque Pacman atteint un certain score, le joueur gagne, et un 2ème niveau apparaît. Celui-ci est différent du premier (différents obstacles, différents fantômes, différents fruits, ...)