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.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):
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.
(3 pts) Ajoutez un power-up d'invincibilité. Créez un acteur qui apparaît rarement (timer de 10 secondes). Si pacman le ramasse, il devient invincible pendant 5 secondes (utilisez un timer). Pendant ce temps, faites clignoter pacman et le fantôme ne peut plus le toucher.
(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.
Partie 3 : fonctionnalités avancées#
...