python pour débutant
- Un buffet de types de données spécialisés – Real Python
- introduction à pytest – Test Python
- Utilisation de Git Subtree pour les projets WordPress • WPShout
- Épisode 227 La science des données maintenable: conseils pour les non-développeurs
- Vos premiers pas dans la science des données en Python – Real Python
Voulez-vous créer vos propres jeux informatiques mais aimez trop Python pour l'abandonner pour une carrière de développeur de jeux? Il y a une solution pour ça! Avec le module Pygame, vous pouvez utiliser vos incroyables compétences Python pour créer des jeux, du plus basique au plus complexe. Ci-dessous, vous apprendrez à utiliser Pygame en créant un clone du jeu Asteroids!
Dans ce didacticiel, vous apprendrez à créer un jeu complet, notamment:
- Chargement images et les afficher à l'écran
- Manutention entrée utilisateur afin de contrôler le jeu
- Objets en mouvement selon le logique du jeu
- Détecter collisions entre objet
- Affichage texte sur l'écran
- En jouant des sons
Cliquez sur le lien ci-dessous pour télécharger le code de ce projet et suivez la création de votre jeu:
Commençons!
Démo: Jeu d'astéroïdes en Python
Le jeu que vous allez créer est un clone du jeu d’arcade classique Asteroids. Dans celui-ci, vous contrôlez un vaisseau spatial et tirez sur des astéroïdes. Si votre vaisseau spatial entre en collision avec un astéroïde, vous perdez. Si vous abattez tous les astéroïdes, vous gagnez!
Aperçu du projet
Votre jeu Asteroids en Python comportera un seul vaisseau spatial. Le vaisseau spatial peut tourner à gauche et à droite ainsi qu'accélérer vers l'avant. Lorsqu'il n’accélère pas, il continuera à se déplacer avec la vitesse qu’il avait. Le vaisseau spatial peut également tirer des balles.
Le jeu utilisera les mappages de touches suivants:
| Clé | action |
|---|---|
| Droit | Faites pivoter le vaisseau spatial vers la droite |
| Gauche | Faites pivoter le vaisseau spatial vers la gauche |
| En haut | Accélérez le vaisseau spatial vers l'avant |
| Espacer | Tirer |
| Esc | Quittez le jeu |
Il y aura également six gros astéroïdes dans le jeu. Lorsqu'une balle frappe un gros astéroïde, elle se divise en deux moyennes. Lorsqu'une balle frappe un astéroïde moyen, elle se divise en deux petits. Un petit astéroïde ne se divise pas mais sera détruit par une balle.
Lorsqu'un astéroïde entre en collision avec le vaisseau spatial, le vaisseau spatial sera détruit et le jeu se terminera par une défaite. Lorsque tous les astéroïdes auront disparu, le jeu se terminera par une victoire!
Le projet sera divisé en dix étapes:
- Configurer Pygame pour un projet Python
- Remise des commentaires dans le jeu
- Charger des images et les afficher à l'écran
- Créer des objets de jeu avec une image, une position et une certaine logique
- Déplacer le vaisseau spatial
- Déplacer les astéroïdes et détecter les collisions avec le vaisseau spatial
- Tirer des balles et détruire des astéroïdes
- Diviser les astéroïdes en astéroïdes plus petits
- Jouer des sons
- Gérer la fin du jeu
Chaque étape fournira des liens vers toutes les ressources nécessaires.
Conditions préalables
Pour créer votre jeu Asteroids, vous aurez besoin d'éléments plus avancés de Python. Vous devriez déjà être à l'aise avec le langage lui-même ainsi qu'avec des concepts tels que les classes, l'héritage et les rappels. Si vous avez besoin de rafraîchir vos connaissances sur ces sujets, consultez notre programmation orientée objet (POO) en Python 3.
Le jeu utilisera également vecteurs pour représenter les positions et les directions, ainsi que certaines opérations vectorielles pour déplacer les éléments sur l'écran. Pygame s'occupera de la plupart des maths, et tous les concepts nécessaires seront expliqués dans ce tutoriel. Cependant, si vous voulez en savoir plus, vous pouvez consulter Ajout de vecteur.
La documentation Pygame peut être utile si vous souhaitez comprendre certains concepts en profondeur, mais vous trouverez tout ce que vous devez savoir dans ce didacticiel.
Étape 1: Configuration de Pygame
À la fin de cette étape, vous aurez un petit projet Python qui utilise Pygame. Il affichera une fenêtre avec une légende, remplie d'une couleur bleue. Ce sera la première étape vers votre jeu Asteroids. Vous n’avez pas besoin d’outils de développement de jeux spécifiques. Votre éditeur de texte préféré et la ligne de commande suffiront.
Projet Python
Pour organiser votre projet, commencez par créer un dossier pour celui-ci:
$ mkdir awesome_pygame_project
$ CD awesome_pygame_project
Comme pour tout projet Python, vous devez également créer un environnement virtuel pour votre jeu Asteroids. Vous pouvez en savoir plus sur les environnements virtuels dans Python Virtual Environments: A Primer.
Lorsque vous avez terminé, créez un requirements.txt fichier et ajoutez une dépendance Pygame. Pour ce projet, il est recommandé d'utiliser la dernière version, qui permettra à votre jeu Asteroids de fonctionner de manière transparente sur Linux et macOS. Votre fichier doit ressembler à ceci:
Ensuite, installez les dépendances:
(venv) $ python -m pip install -r requirements.txt
Vous pouvez vérifier si Pygame a été correctement installé en exécutant cette commande:
(venv) $ python -m pygame.examples.aliens
Si tout s'est bien passé, vous devriez voir une fenêtre avec le jeu Pygame Aliens.
Code Pygame
Il est maintenant temps de commencer à travailler sur votre propre code! En général, la structure d'un programme Pygame ressemble à ceci:
1initialize_pygame()
2
3tandis que Vrai:
4 handle_input()
5 process_game_logic()
6 draw_game_elements()
La ligne 3 démarre une boucle, appelée le boucle de jeu. Chaque itération de cette boucle génère une seule image du jeu et effectue généralement les opérations suivantes:
-
Gestion des entrées: Les entrées telles que les boutons enfoncés, le mouvement de la souris et la position des contrôleurs VR sont collectées puis gérées. Selon le jeu, cela peut amener les objets à changer de position, créer de nouveaux objets, demander la fin du jeu, etc.
-
Logique du jeu: C'est là que la plupart des mécanismes de jeu sont mis en œuvre. Ici, les règles de la physique sont appliquées, les collisions sont détectées et gérées, l'intelligence artificielle fait son travail, etc. Cette partie est également chargée de vérifier si le joueur a gagné ou perdu la partie.
-
Dessin: Si le jeu n’est pas encore terminé, c’est là que le cadre sera dessiné à l’écran. Il comprendra tous les éléments qui sont actuellement dans le jeu et qui sont visibles par le joueur.
La structure générale d'un programme Pygame n'est pas compliquée, et vous pourriez probablement vous en tirer en le mettant dans une boucle de base. Cependant, étant donné que vous pourriez étendre votre jeu Asteroids à l'avenir, il est judicieux d'encapsuler toutes ces opérations dans une classe Python.
Créer une classe signifie que vous devez choisir un nom pour votre jeu, mais «Asteroids» est déjà pris. Que diriez-vous de «Space Rocks»?
Créer un space_rocks répertoire, et à l'intérieur, créez un fichier appelé game.py. C'est ici que vous placerez la classe principale de votre jeu Asteroids: SpaceRocks. Le fichier devrait ressembler à ceci:
1importer pygame
2
3classer SpaceRocks:
4 def __init__(soi):
5 soi._init_pygame()
6 soi.écran = pygame.affichage.mode réglages((800, 600))
7
8 def boucle principale(soi):
9 tandis que Vrai:
dix soi._handle_input()
11 soi._process_game_logic()
12 soi._tirer()
13
14 def _init_pygame(soi):
15 pygame.init()
16 pygame.affichage.set_caption("Space Rocks")
17
18 def _handle_input(soi):
19 passe
20
21 def _process_game_logic(soi):
22 passe
23
24 def _tirer(soi):
25 soi.écran.remplir((0, 0, 255))
26 pygame.affichage.retourner()
Voici ce qui se passe dans le code, étape par étape:
-
Ligne 1 importe le module Pygame pour accéder à toutes ses fonctionnalités étonnantes.
-
Ligne 3 crée le
SpaceRocksclasser. -
Ligne 4 est le constructeur du
SpaceRocksclass, et c’est l’endroit idéal pour placer toutes les méthodes nécessaires à l’initialisation de Pygame. L'initialisation réelle de Pygame se produit dans_init_pygame (). Vous en apprendrez plus sur cette méthode dans un instant. -
Ligne 6 crée une surface d'affichage. Les images dans Pygame sont représentées par les surfaces. Voici quelques choses à savoir à leur sujet:
-
Les surfaces peuvent être dessinées les unes sur les autres, ce qui vous permet de créer des scènes complexes à partir d'images simples.
-
Il y a une surface spéciale dans chaque projet Pygame. Cette surface représente l'écran et est celle qui sera éventuellement affichée aux joueurs. Toutes les autres surfaces doivent être dessinées sur celle-ci à un moment donné. Sinon, ils ne seront pas affichés.
-
Pour créer la surface d'affichage, votre programme utilise
pygame.display.set_mode (). Le seul argument que vous passez à cette méthode est la taille de l'écran, représentée par un tuple de deux valeurs: largeur et hauteur. Dans ce cas, Pygame créera un écran d'une largeur de800pixels et une hauteur de600pixels.
-
-
Ligne 8 est la boucle de jeu décrite ci-dessus. Il contient les trois mêmes étapes pour chaque image:
-
Ligne 10 contient la gestion des entrées.
-
Ligne 11 contient la logique du jeu.
-
Ligne 12 contient un dessin.
-
-
Ligne 14 définit une méthode appelée
_init_pygame (). C'est là qu'une initialisation unique de Pygame se produit. La méthode fait deux choses:-
Ligne 15 appels
pygame.init (). Cette seule ligne de code est responsable de la mise en place des fonctionnalités étonnantes de Pygame. Chaque fois que vous travaillez avec Pygame, vous devez appelerpygame.init ()au début de votre programme pour vous assurer que le framework fonctionnera correctement. -
Ligne 16 définit la légende de votre programme Pygame en utilisant
pygame.display.set_caption (). Dans ce cas, la légende sera le nom de votre jeu:Space Rocks.
-
-
Lignes 18 et 21 définir
_handle_input ()et_process_game_logic (). Ils sont vides pour le moment, mais dans les sections suivantes, vous ajouterez du code pour rendre votre jeu plus intéressant. -
Ligne 24 définit
_tirer(). Cela n'aurait pas beaucoup de sens de créer un modèle pour votre jeu sans rien afficher à l'écran, donc cette méthode a déjà du code. Il est appelé chaque image pour dessiner le contenu de l'écran, et cela en deux étapes:-
Ligne 25 remplit l'écran d'une couleur en utilisant
screen.fill (). La méthode prend un tuple avec trois valeurs, représentant trois couleurs de base: rouge, vert et bleu. Chaque valeur de couleur varie entre0et255, représentant son intensité. Dans cet exemple, un tuple de (0, 0, 255) signifie que la couleur sera uniquement composée de bleu, sans trace de rouge ou de vert. -
Ligne 26 met à jour le contenu de l'écran en utilisant
pygame.display.flip (). Étant donné que votre jeu finira par afficher des objets en mouvement, vous appellerez cette méthode à chaque image pour mettre à jour l'affichage. Pour cette raison, vous devez remplir votre écran de couleur à chaque image, car la méthode effacera le contenu généré lors de l'image précédente.
-
Cela peut sembler beaucoup d'étapes supplémentaires, mais maintenant votre code est bien structuré et comporte des méthodes avec des noms descriptifs. La prochaine fois que vous devrez modifier quelque chose en rapport avec le dessin, vous saurez comment utiliser _tirer(). Pour ajouter la gestion des entrées, vous allez modifier _handle_input (), etc.
Noter: Normalement, vous extrayez des variables telles que la taille et la couleur de l'écran en une constante au début de votre cours. Cependant, en quelques étapes, vous remplacerez la couleur par une image et vous n'utiliserez pas la taille de l'écran en dehors de cette méthode. Pour cette raison, vous pouvez laisser les valeurs telles quelles.
Ensuite, créez un __main__.py fichier dans votre space_rocks dossier. Ce fichier se chargera de créer une nouvelle instance de votre jeu et de la démarrer en exécutant boucle principale(). Ça devrait ressembler à ça:
de Jeu importer SpaceRocks
si __Nom__ == "__principale__":
space_rocks = SpaceRocks()
space_rocks.boucle principale()
La structure de votre projet devrait maintenant ressembler à ceci:
awesome_pygame_project /
|
├── space_rocks /
| ├── __main__.py
| └── game.py
|
└── requirements.txt
Allez-y et lancez le jeu:
(venv) $ python space_rocks
Vous verrez une fenêtre avec un arrière-plan bleu:

Félicitations, vous venez de créer un projet Pygame! Cependant, il n'y a pas de condition de sortie à ce stade, vous devez donc toujours utiliser Ctrl+C dans la ligne de commande pour le quitter. C'est pourquoi vous découvrirez ensuite la gestion des entrées.
Étape 2: Gestion des entrées
À ce stade, vous avez la boucle principale du jeu, prête à être remplie de logique. À la fin de cette étape, vous disposerez également d'un échafaudage pour commencer à brancher les commandes utilisateur.
La plupart des traitements d'entrée dans Pygame se produisent dans un boucle d'événement. Dans chaque image, votre programme peut obtenir une collection d'événements qui se sont produits depuis l'image précédente. Cela inclut les mouvements de la souris, les pressions sur les touches, etc. Ensuite, un par un, ces événements peuvent être traités. Dans Pygame, la méthode pour obtenir cette collection est pygame.event.get ().
L'événement dont vous avez besoin en ce moment est pygame.QUIT. Cela se produit lorsque quelqu'un demande la fin du programme, soit en cliquant sur Fermer ou en appuyant sur Alt+F4 sous Windows et Linux ou Cmd+W sur macOS. Modifier space_rocks / game.py en réécrivant SpaceRocks._handle_input () ainsi:
def _handle_input(soi):
pour un événement dans pygame.un événement.obtenir():
si un événement.taper == pygame.QUITTER:
quitter()
Allez-y et testez-le. Lancez le jeu et cliquez sur le petit X dans le coin ou utilisez un raccourci approprié. La fenêtre sera fermée, comme vous vous en doutez.
Mais vous pouvez aller plus loin. En fin de compte, votre jeu sera contrôlé uniquement avec le clavier, pas la souris. Que diriez-vous de fermer la fenêtre en appuyant sur une touche personnalisée?
Il existe d'autres types d'événements dans Pygame, et l'un d'eux est un événement de pression de touche. Il est représenté par un pygame.KEYDOWN constant. Chaque événement de ce type contient des informations sur la touche enfoncée, stockées dans le event.key attribut. Vous pouvez vérifier les constantes pour différentes clés dans la documentation Pygame. Dans cet exemple, pour fermer le jeu en appuyant sur Esc, vous utiliserez pygame.K_ESCAPE.
Modifier le _handle_input () méthode à nouveau:
def _handle_input(soi):
pour un événement dans pygame.un événement.obtenir():
si un événement.taper == pygame.QUITTER ou (
un événement.taper == pygame.TOUCHE BAS et un événement.clé == pygame.K_ESCAPE
):
quitter()
Maintenant, votre jeu se ferme également lorsque vous appuyez sur Esc.
Vous avez réussi à afficher une fenêtre et à la fermer correctement. Mais la fenêtre est toujours remplie d'une seule couleur. Ensuite, vous apprendrez à charger une image et à l'afficher à l'écran.
Étape 3: Images
À ce stade, vous avez une fenêtre de jeu que vous pouvez fermer en appuyant sur une touche. À la fin de cette étape, vous afficherez une image dans cette fenêtre.
Bien que vous puissiez créer un jeu vidéo avec uniquement des rectangles colorés et d'autres formes simples, l'utilisation d'images le rendra beaucoup plus attrayant. Dans le développement de jeux informatiques, les images sont généralement appelées sprites. Bien sûr, les jeux utilisent beaucoup plus de types de ressources, comme les sons, les polices, les animations, etc. Ensemble, ces ressources sont appelées actifs.
Au fur et à mesure que votre jeu évolue, il est important qu'il conserve une structure appropriée. Alors, commencez par créer un dossier appelé actifs et, à l'intérieur, un autre appelé sprites. C'est là que vous placerez tous les sprites utilisés par votre jeu.
Ensuite, téléchargez l'image de l'arrière-plan de l'espace et placez-la dans le actifs / sprites dossier. Vous pouvez télécharger le code source en cliquant sur le lien ci-dessous:
De plus, comme les images seront chargées plusieurs fois dans votre programme, il est judicieux d'extraire cette fonctionnalité dans une méthode distincte dans un fichier séparé. Créez un fichier appelé space_rocks / utils.py qui conservera toutes les méthodes réutilisables. Ensuite, implémentez le chargement d'image:
1de pygame.image importer charge
2
3def load_sprite(Nom, with_alpha=Vrai):
4 chemin = F"actifs / sprites /Nom.png "
5 load_sprite = charge(chemin)
6
7 si with_alpha:
8 revenir load_sprite.convert_alpha()
9 autre:
dix revenir load_sprite.convertir()
Voici ce qui se passe:
-
Ligne 1 importe une méthode appelée
charge()cela sera nécessaire pour lire les images plus tard. -
Ligne 4 crée un chemin vers une image, en supposant qu'elle est stockée dans le
actifs / spritesrépertoire et qu’il s’agit d’un fichier PNG. De cette façon, vous n'aurez qu'à fournir le nom de l'image-objet ultérieurement. -
Ligne 5 charge l'image en utilisant
charge(). Cette méthode renvoie une surface, qui est un objet utilisé par Pygame pour représenter des images. Vous pourrez ensuite le dessiner à l'écran (ou sur une autre surface, si vous le souhaitez). -
Lignes 8 et 10 convertir l'image dans un format mieux adapté à l'écran pour accélérer le processus de dessin. Ceci est fait avec soit
convert_alpha ()ouconvertir(), selon que vous souhaitez utiliser la transparence.
Noter: En général, vous pouvez simplement utiliser convert_alpha () pour tous les types d'images car il peut également gérer une image sans pixels transparents. Cependant, dessiner des images transparentes est un peu plus lent que dessiner des images non transparentes.
Étant donné que les jeux sur ordinateur sont avant tout une question de performances, vous vous entraînerez à optimiser vos jeux en choisissant le type d'image approprié et en augmentant la vitesse de votre jeu, même si ce n'est que d'un peu.
La structure de votre projet ressemble maintenant à ceci:
awesome_pygame_project /
|
├── actifs /
| |
│ └── sprites /
│ └── space.png
|
├── space_rocks /
│ ├── __main__.py
│ ├── game.py
│ └── utils.py
|
└── requirements.txt
Maintenant que votre programme peut charger des images, il est temps de changer l'arrière-plan bleu en quelque chose de plus intéressant. Modifier le space_rocks / game.py déposer:
importer pygame
de utils importer load_sprite
classer SpaceRocks:
def __init__(soi):
soi._init_pygame()
soi.écran = pygame.affichage.mode réglages((800, 600))
soi.Contexte = load_sprite("espacer", Faux)
def boucle principale(soi):
tandis que Vrai:
soi._handle_input()
soi._process_game_logic()
soi._tirer()
def _init_pygame(soi):
pygame.init()
pygame.affichage.set_caption("Space Rocks")
def _handle_input(soi):
pour un événement dans pygame.un événement.obtenir():
si un événement.taper == pygame.QUITTER ou (
un événement.taper == pygame.TOUCHE BAS et un événement.clé == pygame.K_ESCAPE
):
quitter()
def _process_game_logic(soi):
passe
def _tirer(soi):
soi.écran.blit(soi.Contexte, (0, 0))
pygame.affichage.retourner()
Pour afficher une surface sur une autre dans Pygame, vous devez appeler blit () sur la surface sur laquelle vous souhaitez dessiner. Cette méthode prend deux arguments:
- La surface que vous souhaitez dessiner
- Le point où vous voulez le dessiner
Il est bon de garder à l’esprit que dans Pygame, le système de coordonnées commence dans le coin supérieur gauche. L'axe des x va de gauche à droite et l'axe des y va de haut en bas:

Comme vous pouvez le voir, le EN HAUT vecteur, pointant vers le haut, aura une coordonnée y négative.
Les coordonnées passées à blit () sont donnés sous forme de deux valeurs: X et Oui. Ils représentent le point où le coin supérieur gauche de la surface sera situé après l'opération:

Comme vous pouvez le voir, le coin supérieur gauche est déplacé par les coordonnées blit pour calculer la position correcte.
Dans votre cas, le nouvel arrière-plan a la même taille que l'écran (800 × 600 pixels), donc les coordonnées seront (0, 0), représentant le coin supérieur gauche de l'écran. De cette façon, l'image d'arrière-plan couvrira tout l'écran.
Exécutez votre programme maintenant et vous verrez un écran avec une image d'arrière-plan:

Votre jeu a maintenant une très belle image de fond, mais rien ne se passe encore. Modifions cela en ajoutant des objets.
Étape 4: Contrôle des objets de jeu
À ce stade, votre programme affiche une image d'arrière-plan d'un petit morceau de cosmos où votre jeu Asteroids aura lieu. Il est un peu vide pour le moment, vous allez donc le remplir dans cette section. Vous allez créer une classe qui représente d'autres objets de jeu dessinables et l'utiliser pour montrer un vaisseau spatial et un astéroïde.
Comportement avancé
Vous avez déjà utilisé des surfaces, mais Pygame propose également une autre classe, Lutin, qui est conçue comme une classe de base pour les objets visibles. Il contient des méthodes utiles, mais vous pouvez également rencontrer quelques limitations.
Une limitation est qu'un objet de jeu est plus qu'un simple sprite. Il contient des données supplémentaires, comme sa direction et sa vitesse. Il a également besoin de comportements plus avancés, comme tirer des balles ou jouer des sons. La plupart de ces informations et comportements supplémentaires ne sont pas fournis par le Lutin class, vous devrez donc l'ajouter vous-même.
Un autre problème est que Pygame dessine des sprites à partir du coin supérieur gauche. Dans votre jeu, il peut être plus facile de stocker la position centrale d'un objet dans le but de le déplacer et de le faire pivoter. Dans ce cas, vous devrez mettre en œuvre un moyen de transformer cette position en un coin supérieur gauche comme l'exige Pygame.
Enfin, bien que Pygame dispose déjà de méthodes pour détecter les chevauchements entre les images, elles peuvent ne pas être bonnes pour détecter les collisions entre objets. Un vaisseau spatial rotatif ou un astéroïde ne remplira probablement pas toute l'image, mais plutôt la zone ronde qui s'y trouve. Dans ce cas, la collision ne doit prendre en compte que cette zone ronde, pas toute la surface du sprite. Sinon, vous risquez d'obtenir des résultats incorrects:

Dans cet exemple, les sprites entrent en collision, mais pas les objets du jeu.
C'est en fait là que le Lutin la classe pourrait vous aider, car vous pouvez l'utiliser avec pygame.sprite.collide_circle (). Cette méthode détecte les collisions entre deux sprites à l'aide de cercles centrés sur leurs surfaces. Cependant, détecter une collision de cercles n'est pas un processus très compliqué et vous pouvez le mettre en œuvre vous-même.
Compte tenu de ces problèmes, il devient rapidement évident que le Pygame intégré Lutin La classe est destinée à être augmentée, pas simplement utilisée seule. Dans le cas de votre jeu, les sprites Pygame fournissent quelques fonctionnalités utiles. Il peut être judicieux d'implémenter à la place une classe personnalisée pour les objets de jeu. Cela devrait vous donner plus de contrôle et vous aider à comprendre certains concepts puisque vous les implémenterez vous-même.
Classe GameObject
Dans cette section, vous présenterez le GameObject classer. Il encapsulera certains comportements et données génériques pour tous les autres objets du jeu. Les classes qui représentent des objets spécifiques (comme le vaisseau spatial) en hériteront et l'étendront avec leur propre comportement et leurs propres données. Si vous souhaitez actualiser vos connaissances sur les classes et l'héritage, consultez la programmation orientée objet (POO) dans Python 3.
Le GameObject class stockera les données suivantes:
positionner: Un point au centre de l'objet sur l'écran 2Dlutin: Une image utilisée pour afficher l'objetrayon: Une valeur représentant la zone de collision autour de la position de l'objetrapidité: Une valeur utilisée pour le mouvement
Voici une représentation graphique de l'objet du jeu:

Le lutin sera une surface chargée de load_sprite () à partir des exemples précédents. Le rayon est un entier indiquant le nombre de pixels du centre de l'objet au bord de la zone de collision. Cependant, le positionner lui-même et le rapidité aura besoin d'un nouveau type: a vecteur.
Les vecteurs sont similaires aux tuples. Dans un monde 2D (comme celui de votre jeu), les vecteurs sont représentés par deux valeurs indiquant les coordonnées x et y. Ces coordonnées peuvent pointer vers une position, mais elles peuvent également représenter un mouvement ou une accélération dans une direction donnée. Des vecteurs peuvent être ajoutés, soustraits ou même multipliés pour mettre à jour rapidement la position d'un sprite. Vous pouvez en savoir plus sur les vecteurs dans les vecteurs dans l'espace à 2 dimensions.
En raison de l'utilité des vecteurs dans les jeux, Pygame a déjà une classe pour eux: Vecteur2 dans le pygame.math module. Il offre des fonctionnalités supplémentaires, comme le calcul de la distance entre les vecteurs et l'ajout ou la soustraction de vecteurs. Ces fonctionnalités rendront votre logique de jeu beaucoup plus facile à implémenter.
Dans le space_rocks répertoire, créez un nouveau fichier appelé models.py. Pour l'instant, il stockera le GameObject classe, mais plus tard, vous ajouterez des classes pour les astéroïdes, les balles et le vaisseau spatial. Le fichier devrait ressembler à ceci:
1de pygame.math importer Vecteur2
2
3classer GameObject:
4 def __init__(soi, positionner, lutin, rapidité):
5 soi.positionner = Vecteur2(positionner)
6 soi.lutin = lutin
7 soi.rayon = lutin.get_width() / 2
8 soi.rapidité = Vecteur2(rapidité)
9
dix def tirer(soi, surface):
11 blit_position = soi.positionner - Vecteur2(soi.rayon)
12 surface.blit(soi.lutin, blit_position)
13
14 def bouge toi(soi):
15 soi.positionner = soi.positionner + soi.rapidité
16
17 def collision_avec(soi, autre_obj):
18 distance = soi.positionner.distance_to(autre_obj.positionner)
19 revenir distance < soi.rayon + autre_obj.rayon
Voici une ventilation:
-
Ligne 1 importe le
Vecteur2classe mentionnée précédemment. -
Ligne 3 crée le
GameObjectclass, que vous utiliserez pour représenter tous les objets du jeu dans Space Rocks. -
Ligne 4 est le constructeur du
GameObjectclasser. Il a besoin de trois arguments:-
positionner: Le centre de l'objet -
lutin: L'image utilisée pour dessiner cet objet -
rapidité: Met à jour lepositionnerde l'objet chaque image
-
-
Lignes 5 et 8 assurez-vous que le
positionneret lerapiditéseront toujours représentés comme des vecteurs pour les calculs futurs, même si les tuples sont passés au constructeur. Vous faites cela en appelant leVecteur2 ()constructeur. Si un tuple lui est attribué, il en créera un nouveau vecteur. Si un vecteur lui est attribué, il créera une copie de ce vecteur. -
Ligne 7 calcule le
rayonégale à la moitié de la largeur dulutinimage. Dans ce programme, les sprites d'objets de jeu seront toujours des carrés avec un arrière-plan transparent. Vous pouvez également utiliser la hauteur de l'image – cela ne ferait aucune différence. -
Ligne 10 définit
tirer(), qui dessinera l’objetlutinen surface passé comme argument. -
Ligne 11 calcule la position correcte pour le blitting de l'image. Le processus est décrit plus en détail ci-dessous. Notez que le
Vecteur2 ()Le constructeur reçoit un seul nombre au lieu d'un tuple. Dans ce cas, il utilisera ce nombre pour les deux valeurs. AlorsVector2 (self.radius)est l'équivalent deVector2 ((self.radius, self.radius)). -
Ligne 12 utilise la position de Blit nouvellement calculée pour placer l'image-objet de votre objet à un endroit correct sur la surface donnée.
-
Ligne 14 définit
bouge toi(). Cela mettra à jour la position de l'objet de jeu. -
Ligne 15 ajoute la vitesse à la position et obtient en conséquence un vecteur de position mis à jour. Pygame simplifie la manipulation des vecteurs, vous permettant de les ajouter comme des nombres.
-
Ligne 17 définit le
collision_avec ()méthode, qui sera utilisée pour détecter les collisions. -
Ligne 18 calcule la distance entre deux objets en utilisant
Vector2.distance_to (). -
Ligne 19 vérifie si cette distance est inférieure à la somme des rayons des objets. Si tel est le cas, les objets entrent en collision.
Veuillez noter que vos objets de jeu ont une position centrale, mais blit () nécessite un coin supérieur gauche. Ainsi, la position blit doit être calculée en déplaçant la position réelle de l'objet par un vecteur:

Ce processus se déroule dans tirer().
Vous pouvez tester cela en ajoutant un vaisseau spatial et un seul astéroïde. Tout d'abord, copiez les images du vaisseau spatial et de l'astéroïde actifs / sprites. Vous pouvez télécharger le code source en cliquant sur le lien ci-dessous:
La structure de votre projet doit ressembler à ceci:
awesome_pygame_project /
|
├── actifs /
| |
│ └── sprites /
│ ├── asteroid.png
│ ├── space.png
│ └── vaisseau spatial.png
|
├── space_rocks /
│ ├── __main__.py
│ ├── game.py
│ ├── models.py
│ └── utils.py
|
└── requirements.txt
Modifiez maintenant le space_rocks / game.py déposer:
importer pygame
de des modèles importer GameObject
de utils importer load_sprite
classer SpaceRocks:
def __init__(soi):
soi._init_pygame()
soi.écran = pygame.affichage.mode réglages((800, 600))
soi.Contexte = load_sprite("espacer", Faux)
soi.vaisseau spatial = GameObject(
(400, 300), load_sprite("vaisseau spatial"), (0, 0)
)
soi.astéroïde = GameObject(
(400, 300), load_sprite("astéroïde"), (1, 0)
)
def boucle principale(soi):
tandis que Vrai:
soi._handle_input()
soi._process_game_logic()
soi._tirer()
def _init_pygame(soi):
pygame.init()
pygame.affichage.set_caption("Space Rocks")
def _handle_input(soi):
pour un événement dans pygame.un événement.obtenir():
si un événement.taper == pygame.QUITTER ou (
un événement.taper == pygame.TOUCHE BAS et un événement.clé == pygame.K_ESCAPE
):
quitter()
def _process_game_logic(soi):
soi.vaisseau spatial.bouge toi()
soi.astéroïde.bouge toi()
def _tirer(soi):
soi.écran.blit(soi.Contexte, (0, 0))
soi.vaisseau spatial.tirer(soi.écran)
soi.astéroïde.tirer(soi.écran)
pygame.affichage.retourner()
Les deux objets sont placés au milieu de l'écran, en utilisant les coordonnées (400, 300). La position des deux objets sera mise à jour à chaque image en utilisant _process_game_logic (), et ils seront dessinés en utilisant _tirer().
Exécutez ce programme et vous verrez un astéroïde se déplacer vers la droite et un vaisseau spatial immobile au milieu de l'écran:

Vous pouvez également tester collision_avec () en ajoutant temporairement une ligne à la fin de _tirer():
impression("Collision:", soi.vaisseau spatial.collision_avec(soi.astéroïde))
Dans la ligne de commande, vous remarquerez comment la méthode s’imprime initialement Vrai since the asteroid covers the spaceship. Later, as the asteroid moves further to the right, it starts printing False.
Controlling the Speed
Now that you have moving objects on the screen, it’s time to think about how your game will perform on different machines with different processors. Sometimes it’ll run faster, and sometimes it’ll run slower.
Because of that, the asteroids (and soon bullets) will move with different speed, making the game sometimes easier and sometimes harder. That’s not something that you want. What you want is for your game to run with a fixed number of frames per second (FPS).
Luckily, Pygame can take care of that. It offers a pygame.time.Clock class with a tick() method. This method will wait long enough to match the desired FPS value, passed as an argument.
Go ahead and update space_rocks/game.py:
import pygame
de models import GameObject
de utils import load_sprite
classer SpaceRocks:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.Contexte = load_sprite("space", False)
self.l'horloge = pygame.time.Clock()
self.spaceship = GameObject(
(400, 300), load_sprite("spaceship"), (0, 0)
)
self.astéroïde = GameObject(
(400, 300), load_sprite("asteroid"), (1, 0)
)
def main_loop(self):
while True:
self._handle_input()
self._process_game_logic()
self._draw()
def _init_pygame(self):
pygame.init()
pygame.display.set_caption("Space Rocks")
def _handle_input(self):
pour un événement dans pygame.un événement.get():
if un événement.taper == pygame.QUIT ou (
un événement.taper == pygame.KEYDOWN et un événement.clé == pygame.K_ESCAPE
):
quit()
def _process_game_logic(self):
self.spaceship.move()
self.astéroïde.move()
def _draw(self):
self.screen.blit(self.Contexte, (0, 0))
self.spaceship.tirer(self.screen)
self.astéroïde.tirer(self.screen)
pygame.display.retourner()
self.l'horloge.cocher(60)
If you run your game now, then the asteroid might move at a different speed than it initially had. However, you can now be sure that this speed will remain the same, even on computers with super-fast processors. That’s because your game will always run at 60 FPS. You can also experiment with different values passed to tick() to see the difference.
You just learned how to show and move objects on the screen. Now you can add some more advanced logic to your game.
Step 5: Spaceship
At this point, you should have a class for general drawable and movable game objects. At the end of this step, you’ll use it to create a controllable spaceship.
The class you created in the previous step, GameObject, holds some general logic that can be reused by different game objects. However, each game object will also implement its own logic. The spaceship, for example, is expected to rotate and accelerate. It will also shoot bullets, but that comes later.
Creating a Class
The image of the spaceship is already in the space_rocks/assets directory that you added in Step 4. However, earlier it was used in the main game file, and now you need to load it in one of the models. To be able to do this, update the imports section in the space_rocks/models.py file:
de pygame.math import Vector2
de utils import load_sprite
Now you can create, in the same file, the Spaceship class that inherits from GameObject:
classer Spaceship(GameObject):
def __init__(self, positionner):
super().__init__(positionner, load_sprite("spaceship"), Vector2(0))
It doesn’t do a lot at this point—it just calls the GameObject constructor with a specific image and a zero velocity. However, you’ll soon add more functionality.
To use this new class, you first need to import it. Update the imports in the space_rocks/game.py file like this:
import pygame
de models import Spaceship
de utils import load_sprite
You probably noticed that the original import of the GameObject class is gone. That’s because GameObject is used as a base class to be inherited by other classes. You shouldn’t use it directly, but rather import the classes that represent actual game objects.
This means that the asteroid from the previous step will stop working, but that’s not a big issue. You’ll soon add a proper class that represents asteroids. Until then, you should focus on the spaceship.
Go ahead and edit the SpaceRocks class to look like this:
1classer SpaceRocks:
2 def __init__(self):
3 self._init_pygame()
4 self.screen = pygame.display.set_mode((800, 600))
5 self.Contexte = load_sprite("space", False)
6 self.l'horloge = pygame.time.Clock()
7 self.spaceship = Spaceship((400, 300))
8
9 def main_loop(self):
dix while True:
11 self._handle_input()
12 self._process_game_logic()
13 self._draw()
14
15 def _init_pygame(self):
16 pygame.init()
17 pygame.display.set_caption("Space Rocks")
18
19 def _handle_input(self):
20 pour un événement dans pygame.un événement.get():
21 if un événement.taper == pygame.QUIT ou (
22 un événement.taper == pygame.KEYDOWN et un événement.clé == pygame.K_ESCAPE
23 ):
24 quit()
25
26 def _process_game_logic(self):
27 self.spaceship.move()
28
29 def _draw(self):
30 self.screen.blit(self.Contexte, (0, 0))
31 self.spaceship.tirer(self.screen)
32 pygame.display.retourner()
33 self.l'horloge.cocher(60)
Two things happened:
-
In line 7, you replaced the base
GameObjectclass with a dedicatedSpaceshipclass. -
You removed all
self.asteroidreferences from__init__(),_process_game_logic()et_draw().
If you run your game now, then you’ll see a spaceship in the middle of the screen:

The changes didn’t add any new behavior yet, but now you have a class that you can extend.
Rotating the Spaceship
By default, the spaceship is facing up, toward the top of the screen. Your players should be able to rotate it left and right. Luckily, Pygame has built-in methods for rotating sprites, but there’s a small problem.
In general, image rotation is a complex process that requires recalculating pixels in the new image. During that recalculation, information about the original pixels is lost and the image is deformed a bit. With each rotation, the deformation becomes more and more visible.
Because of that, it might be a better idea to store the original sprite in the Spaceship class and have another sprite, which will be updated every time the spaceship rotates.
For that approach to work, you’ll need to know the angle by which the spaceship is rotated. This can be done in two ways:
- Keep the angle as a floating point value and update it during rotation.
- Keep the vector representing the direction the spaceship is facing and calculate the angle using that vector.
Both ways are good, but you need to pick one before you proceed. Since the position and the velocity of the spaceship are already vectors, it makes sense to use another vector to represent the direction. That will make it more straightforward to add vectors and update the position later. Heureusement, le Vector2 class can be rotated very easily, and the result won’t be deformed.
First, create a constant vector called EN HAUT dans le space_rocks/models.py file. You’ll use it as a reference later:
Remember that Pygame’s y-axis goes from top to bottom, so a negative value actually points upwards:

Next, modify the Spaceship class:
classer Spaceship(GameObject):
MANEUVERABILITY = 3
Le MANEUVERABILITY value determines how fast your spaceship can rotate. You learned earlier that vectors in Pygame can be rotated, and this value represents an angle in degrees by which your spaceship’s direction can rotate each frame. Using a larger number will rotate the spaceship faster, while a smaller number will allow more granular control over the rotation.
Next, add a direction to the Spaceship class by modifying the constructor:
def __init__(self, positionner):
# Make a copy of the original UP vector
self.direction = Vector2(EN HAUT)
super().__init__(positionner, load_sprite("spaceship"), Vector2(0))
The direction vector will initially be the same as the EN HAUT vector. However, it will be modified later, so you need to create a copy of it.
Next, you need to create a new method in the Spaceship class called rotate():
def rotate(self, clockwise=True):
sign = 1 if clockwise else -1
angle = self.MANEUVERABILITY * sign
self.direction.rotate_ip(angle)
This method will change the direction by rotating it either clockwise or counterclockwise. Le rotate_ip() method of the Vector2 class rotates it in place by a given angle in degrees. The length of the vector doesn’t change during this operation. You can learn a bit more about the advanced math behind 2D vector rotation from Rotating Points Using Rotation Matrices.
All that’s left is to update the drawing of the Spaceship. To do this, you first need to import rotozoom, which is responsible for scaling and rotating images:
de pygame.math import Vector2
de pygame.transform import rotozoom
de utils import load_sprite, wrap_position
Then, you can override the draw() method in the Spaceship class:
1def tirer(self, surface):
2 angle = self.direction.angle_to(EN HAUT)
3 rotated_surface = rotozoom(self.sprite, angle, 1.0)
4 rotated_surface_size = Vector2(rotated_surface.get_size())
5 blit_position = self.positionner - rotated_surface_size * 0,5
6 surface.blit(rotated_surface, blit_position)
Here’s a step-by-step breakdown:
-
Ligne 2 uses the
angle_to()method of theVector2class to calculate the angle by which one vector needs to be rotated in order to point in the same direction as the other vector. This makes it painless to translate the spaceship’s direction into the rotation angle in degrees. -
Ligne 3 rotates the sprite using
rotozoom(). It takes the original image, the angle by which it should be rotated, and the scale that should be applied to the sprite. In this case, you don’t want to change the size, so you keep the scale as1.0. -
Lines 4 and 5 recalculate the blit position, using the size of
rotated_surface. The process is described below. -
Ligne 5 contains the
rotated_surface_size * 0.5opération. That’s another thing you can do with vectors in Pygame. When you multiply a vector by a number, all its coordinates are multiplied by that number. As a result, multiplying by0,5will return a vector with half the length of the original. -
Ligne 6 uses the newly calculated blit position to put the image on the screen.
Note that rotozoom() returns a new surface with a rotated image. However, in order to keep all the contents of the original sprite, the new image might have a different size. In that case, Pygame will add some additional, transparent background:

The size of the new image can be significantly different than that of the original image. That’s why draw() recalculates the blit position of rotated_surface. Souviens-toi que blit() starts in the upper-left corner, so to center the rotated image, you also need to move the blit position by half the size of the image.
Now you need to add input handling. However, the event loop won’t exactly work here. Events are recorded when they happen, but you need to constantly check if a key is pressed. After all, the spaceship should accelerate for as long as you press Up, and it should rotate constantly when you press Gauche ou Droit.
You could create a flag for each key, set it when the key is pressed, and reset when it’s released. However, there’s a better way.
The current state of the keyboard is stored in Pygame and can be obtained using pygame.key.get_pressed(). It returns a dictionary where key constants (like pygame.K_ESCAPE that you used previously) are keys, and the value is True if the key is pressed or False otherwise.
Knowing this, you can edit the space_rocks/game.py file and update the _handle_input() method of SpaceRocks class. The constants you need to use for arrow keys are pygame.K_RIGHT et pygame.K_LEFT:
def _handle_input(self):
pour un événement dans pygame.un événement.get():
if un événement.taper == pygame.QUIT ou (
un événement.taper == pygame.KEYDOWN et un événement.clé == pygame.K_ESCAPE
):
quit()
is_key_pressed = pygame.clé.get_pressed()
if is_key_pressed[[[[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[[[[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
Now your spaceship will rotate left and right when you press arrow keys:

As you can see, the spaceship rotates correctly. However, it still doesn’t move. You’ll fix that next.
Accelerating the Spaceship
In this section, you’ll add acceleration to your spaceship. Remember that, according to the game mechanics of Asteroids, the spaceship can only move forward.
In your game, when you press Up, the spaceship’s speed will increase. When you release the key, the spaceship will maintain its current speed but should no longer accelerate. So in order to slow it down, you’ll have to turn the spaceship around and press Up de nouveau.
The process might already seem a bit complicated, so before you proceed, here is a short recap:
directionis a vector describing where the spaceship is pointing.velocityis a vector describing where the spaceship moves each frame.ACCELERATIONis a constant number describing how fast the spaceship can speed up each frame.
You can calculate the change in velocity by multiplying the direction vector by the ACCELERATION value and adding the result to the current velocity. This happens only when the engine is on—that is, when the player presses Up. The new position of the spaceship is calculated by adding the current velocity to the current position of the spaceship. This happens each frame, regardless of the engine status.
Knowing this, you can add the ACCELERATION value to the Spaceship class:
classer Spaceship(GameObject):
MANEUVERABILITY = 3
ACCELERATION = 0,25
Then, create accelerate() dans le Spaceship class:
def accelerate(self):
self.velocity += self.direction * self.ACCELERATION
Now you can add input handling to _handle_input() dans SpaceRocks. Similarly to the rotation, this will check the current state of the keyboard, not the keypress events. The constant for Up is pygame.K_UP:
def _handle_input(self):
pour un événement dans pygame.un événement.get():
if un événement.taper == pygame.QUIT ou (
un événement.taper == pygame.KEYDOWN et un événement.clé == pygame.K_ESCAPE
):
quit()
is_key_pressed = pygame.clé.get_pressed()
if is_key_pressed[[[[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[[[[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[[[[pygame.K_UP]:
self.spaceship.accelerate()
Go ahead and test this. Run your game, rotate the spaceship, and turn on the engine:

Your spaceship can now move and rotate! However, when it reaches the edge of the screen, it just keeps moving. That’s something you should fix!
Wrapping Objects Around the Screen
An important element of this game is making sure that game objects don’t leave the screen. You can either have them bounce back off the edge or make them reappear on the opposite edge of the screen. In this project, you’ll implement the latter.
Start by importing the Vector2 class in space_rocks/utils.py file:
de pygame.image import load
de pygame.math import Vector2
Next, create wrap_position() in the same file:
1def wrap_position(positionner, surface):
2 X, y = positionner
3 w, h = surface.get_size()
4 revenir Vector2(X % w, y % h)
By using the modulo operator on line 4, you make sure that the position never leaves the area of the given surface. In your game, that surface will be the screen.
Import this new method in space_rocks/models.py:
de pygame.math import Vector2
de pygame.transform import rotozoom
de utils import load_sprite, wrap_position
Now you can update move() dans le GameObject class:
def move(self, surface):
self.positionner = wrap_position(self.positionner + self.velocity, surface)
Notice that using the wrap_position() isn’t the only change here. You also add a new surface argument to this method. That’s because you need to know the area around which the position should be wrapped. Remember to update the method call in the SpaceRocks class as well:
def _process_game_logic(self):
self.spaceship.move(self.screen)
Now your spaceship reappears on the other side of the screen.
The logic of moving and rotating the spaceship is ready. But the ship is still alone in the empty space. Time to add some asteroids!
Step 6: Asteroids
At this point, you have a single spaceship that you can move on the screen. At the end of this step, your game will also show some asteroids. Moreover, you’re going to implement collisions between the spaceship and the asteroids.
Creating a Class
Semblable à Spaceship, you’ll start by creating a class called Astéroïde that inherits from GameObject. Edit the space_rocks/models.py file like so:
classer Astéroïde(GameObject):
def __init__(self, positionner):
super().__init__(positionner, load_sprite("asteroid"), (0, 0))
Just like before, you start by calling the GameObject constructor with a specific image. You added the image in one of the previous steps.
Next, import the new class in space_rocks/game.py:
import pygame
de models import Astéroïde, Spaceship
de utils import load_sprite
Finally, edit the constructor of the SpaceRocks class in the same file to create six asteroids:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.Contexte = load_sprite("space", False)
self.l'horloge = pygame.time.Clock()
self.astéroïdes = [[[[Astéroïde(0, 0) pour _ dans gamme(6)]
self.spaceship = Spaceship((400, 300))
Now that you have more game objects, it would be a good idea to create a helper method in the SpaceRocks class that returns all of them. This method will then be used by the drawing and moving logic. That way, you can later introduce new types of game objects and modify only this single method, or you can exclude some objects from this group if necessary.
Call this method _get_game_objects():
def _get_game_objects(self):
revenir [[[[*self.astéroïdes, self.spaceship]
Now use it to move all game objects in a single loop by editing _process_game_logic():
def _process_game_logic(self):
pour game_object dans self._get_game_objects():
game_object.move(self.screen)
Il en va de même _draw():
def _draw(self):
self.screen.blit(self.Contexte, (0, 0))
pour game_object dans self._get_game_objects():
game_object.tirer(self.screen)
pygame.display.retourner()
self.l'horloge.cocher(60)
Run your game now and you should see a screen with the asteroids:

Unfortunately, all the asteroids are piled up in one corner of the screen.
Well, that was kind of expected, since all the asteroids are created with a position of (0, 0), which represents the top-left corner. You can change this by setting a random position on the screen.
Randomizing the Position
To generate a random position, you’ll have to add some imports to space_rocks/utils.py file:
import random
de pygame.image import load
de pygame.math import Vector2
Then, create a method called get_random_position() in the same file:
def get_random_position(surface):
revenir Vector2(
random.randrange(surface.get_width()),
random.randrange(surface.get_height()),
)
This will generate a random set of coordinates on a given surface and return the result as a Vector2 instance.
Next, import this method in the space_rocks/game.py file:
import pygame
de models import Astéroïde, Spaceship
de utils import get_random_position, load_sprite
Now use get_random_position() to place all six asteroids in random locations. Modify the constructor of the SpaceRocks class:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.Contexte = load_sprite("space", False)
self.l'horloge = pygame.time.Clock()
self.astéroïdes = [[[[
Astéroïde(get_random_position(self.screen)) pour _ dans gamme(6)
]
self.spaceship = Spaceship((400, 300))
Now when you run the game, you’ll see a nice, random distribution of asteroids on the screen:

This looks much better, but there’s a small problem: the asteroids were generated in the same area as the spaceship. After you add collisions, this would cause the player to lose immediately after starting the game. That would be very unfair!
One solution to this problem is to check if the position is too close to the spaceship, and if so, generate a new one until a valid position is found.
Start by creating a constant representing an area that has to remain empty. A value of 250 pixels should be enough:
classer SpaceRocks:
MIN_ASTEROID_DISTANCE = 250
Now you can modify the constructor of the SpaceRocks class to make sure that your players always have a chance to win:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.Contexte = load_sprite("space", False)
self.l'horloge = pygame.time.Clock()
self.astéroïdes = []
self.spaceship = Spaceship((400, 300))
pour _ dans gamme(6):
while True:
positionner = get_random_position(self.screen)
if (
positionner.distance_to(self.spaceship.positionner)
> self.MIN_ASTEROID_DISTANCE
):
Pause
self.astéroïdes.append(Astéroïde(positionner))
In a loop, your code checks if the position of an asteroid is larger than the minimal asteroid distance. If not, then the loop runs again until such a position is found.
Run the program again, and none of the asteroids will overlap with the spaceship:

You can run the game several times to make sure that each time there’s some free space around the spaceship.
Moving the Asteroids
At the moment, your program shows six asteroids at random positions, and you’re ready to spice things up a bit by moving them! Similar to the position, the velocity of an asteroid should also be random, not only in terms of the direction, but also value.
Start by creating a method called get_random_velocity() dans le space_rocks/utils.py file:
def get_random_velocity(min_speed, max_speed):
speed = random.randint(min_speed, max_speed)
angle = random.randrange(0, 360)
revenir Vector2(speed, 0).rotate(angle)
The method will generate a random value between min_speed et max_speed and a random angle between 0 and 360 degrees. Then it will create a vector with that value, rotated by that angle.
Because the asteroid’s velocity should be random no matter where it is placed, let’s use this method directly in the Astéroïde class. Start with updating the imports in the space_rocks/models.py file:
de pygame.math import Vector2
de pygame.transform import rotozoom
de utils import get_random_velocity, load_sprite, wrap_position
Note that you’re setting a your random position in one place and your random velocity somewhere else. That’s because the position should be random only for the six asteroids you start with, so it’s being set in in the space_rocks/game.py file, where the game is initialized. However, the velocity is random for every asteroid, so you set it in the constructor of the Astéroïde class.
Then use the new method in the constructor of the Astéroïde class:
def __init__(self, positionner):
super().__init__(
positionner, load_sprite("asteroid"), get_random_velocity(1, 3)
)
Notice that the method uses the minimum value of 1. That’s because the asteroid should always move, at least a bit.
Run your game again to see moving asteroids:
You can also move the spaceship around the screen. Unfortunately, when it encounters an asteroid, nothing happens. It’s time to add some collisions.
Colliding With the Spaceship
A very important part of this game is the possibility of your spaceship being destroyed by an asteroid collision. You can check the collisions using GameObject.collides_with() introduced in Step 4. All you need to do it call this method for each asteroid.
Edit the _process_game_logic() method in the SpaceRocks class like this:
def _process_game_logic(self):
pour game_object dans self._get_game_objects():
game_object.move(self.screen)
if self.spaceship:
pour astéroïde dans self.astéroïdes:
if astéroïde.collides_with(self.spaceship):
self.spaceship = Aucun
Pause
If any of the asteroids collides with the spaceship, then the spaceship is destroyed. In this game, you’ll represent this by setting self.spaceship à Aucun.
Notice that there’s also a check for self.spaceship at the beginning of the loop. That’s because, when the spaceship is destroyed, there’s no reason to check any collisions with it. Also, detecting a collision with a Aucun object would result in an error.
Now that it’s possible for the spaceship to have a value of Aucun, it’s important to update _get_game_objects() dans le SpaceRocks class to avoid trying to render or move a destroyed spaceship:
def _get_game_objects(self):
game_objects = [[[[*self.astéroïdes]
if self.spaceship:
game_objects.append(self.spaceship)
revenir game_objects
The same goes for input handling:
def _handle_input(self):
pour un événement dans pygame.un événement.get():
if un événement.taper == pygame.QUIT ou (
un événement.taper == pygame.KEYDOWN et un événement.clé == pygame.K_ESCAPE
):
quit()
is_key_pressed = pygame.clé.get_pressed()
if self.spaceship:
if is_key_pressed[[[[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[[[[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[[[[pygame.K_UP]:
self.spaceship.accelerate()
You can run your game now and see that the spaceship disappears after colliding with an asteroid:

Your spaceship can now fly around and be destroyed when it collides with asteroids. You’re ready to make it possible for the asteroids to be destroyed too.
Step 7: Bullets
At this point, you have some randomly placed and moving asteroids and a spaceship that can move around and avoid them. At the end of this step, your spaceship will also be able to defend itself by shooting bullets.
Creating a Class
Start with adding an image of a bullet to assets/sprites. You can download the source code by clicking the link below:
The structure of your project should look like this:
awesome_pygame_project/
|
├── assets/
| |
│ └── sprites/
│ ├── asteroid.png
│ ├── bullet.png
│ ├── space.png
│ └── spaceship.png
|
├── space_rocks/
│ ├── __main__.py
│ ├── game.py
│ ├── models.py
│ └── utils.py
|
└── requirements.txt
Then edit the space_rocks/models.py file by creating a class called Balle that inherits from GameObject:
classer Balle(GameObject):
def __init__(self, positionner, velocity):
super().__init__(positionner, load_sprite("bullet"), velocity)
Just like before, this will only call the GameObject constructor with a specific sprite. However, this time the velocity will be a required argument because a bullet has to move.
Next, you should add a way to keep track of the bullets, similar to what you did for the asteroids. Edit the constructor of the SpaceRocks class in the space_rocks/game.py file:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.Contexte = load_sprite("space", False)
self.l'horloge = pygame.time.Clock()
self.astéroïdes = []
self.bullets = []
self.spaceship = Spaceship((400, 300))
pour _ dans gamme(6):
while True:
positionner = get_random_position(self.screen)
if (
positionner.distance_to(self.spaceship.positionner)
> self.MIN_ASTEROID_DISTANCE
):
Pause
self.astéroïdes.append(Astéroïde(positionner))
Bullets should be treated the same way as other game objects, so edit the _get_game_object() method in SpaceRocks:
def _get_game_objects(self):
game_objects = [[[[*self.astéroïdes, *self.bullets]
if self.spaceship:
game_objects.append(self.spaceship)
revenir game_objects
The list of bullets is there, but it’s empty for now. You can fix that.
Shooting a Bullet
There’s a small issue with shooting. Bullets are stored in the main game object, represented by the SpaceRocks class. However, the shooting logic should be determined by the spaceship. It’s the spaceship that knows how to create a new bullet, but it’s the game that stores and later animates the bullets. Le Spaceship class needs a way to inform the SpaceRocks class that a bullet has been created and should be tracked.
To fix this, you can add a callback function to the Spaceship class. That function will be provided by the SpaceRocks class when the spaceship is initialized. Every time the spaceship creates a bullet, it will initialize a Balle object and then call the callback. The callback will add the bullet to the list of all bullets stored by the game.
Start by adding a callback to the constructor of the Spaceship class in the space_rocks/models.py file:
def __init__(self, positionner, create_bullet_callback):
self.create_bullet_callback = create_bullet_callback
# Make a copy of the original UP vector
self.direction = Vector2(EN HAUT)
super().__init__(positionner, load_sprite("spaceship"), Vector2(0))
You’ll also need the value of the bullet’s speed:
classer Spaceship(GameObject):
MANEUVERABILITY = 3
ACCELERATION = 0,25
BULLET_SPEED = 3
Next, create a method called shoot() dans le Spaceship class:
def shoot(self):
bullet_velocity = self.direction * self.BULLET_SPEED + self.velocity
balle = Balle(self.positionner, bullet_velocity)
self.create_bullet_callback(balle)
You start by calculating the velocity of the bullet. The bullet is always shot forward, so you use the direction of the spaceship multiplied by the speed of the bullet. Because the spaceship doesn’t necessarily stand still, you add its velocity to the velocity of the bullet. That way, you can create high-speed bullets if the spaceship is moving very fast.
Then you create an instance of the Balle class at the same location as the spaceship, using the velocity that was just calculated. Finally, the bullet is added to all the bullets in the game by using the callback method.
Now add the callback to the spaceship when it’s created. Bullets are stored as a list, and the only thing the callback has to do is add new items to that list. Therefore, the append() method should do the job. Edit the constructor of the SpaceRocks class in the space_rocks/game.py file:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.Contexte = load_sprite("space", False)
self.l'horloge = pygame.time.Clock()
self.astéroïdes = []
self.bullets = []
self.spaceship = Spaceship((400, 300), self.bullets.append)
pour _ dans gamme(6):
while True:
positionner = get_random_position(self.screen)
if (
positionner.distance_to(self.spaceship.positionner)
> self.MIN_ASTEROID_DISTANCE
):
Pause
self.astéroïdes.append(Astéroïde(positionner))
The last thing you need to add is input handling. The bullet should be generated only when Espacer pressed, so you can use the event loop. The constant for Espacer is pygame.K_SPACE.
Modify the _handle_input() method in the SpaceRocks class:
def _handle_input(self):
pour un événement dans pygame.un événement.get():
if un événement.taper == pygame.QUIT ou (
un événement.taper == pygame.KEYDOWN et un événement.clé == pygame.K_ESCAPE
):
quit()
elif (
self.spaceship
et un événement.taper == pygame.KEYDOWN
et un événement.clé == pygame.K_SPACE
):
self.spaceship.shoot()
is_key_pressed = pygame.clé.get_pressed()
if self.spaceship:
if is_key_pressed[[[[pygame.K_RIGHT]:
self.spaceship.rotate(clockwise=True)
elif is_key_pressed[[[[pygame.K_LEFT]:
self.spaceship.rotate(clockwise=False)
if is_key_pressed[[[[pygame.K_UP]:
self.spaceship.accelerate()
Notice that the new input handling also checks if the spaceship exists. Otherwise, you could encounter errors when trying to call shoot() on a Aucun object.
Run your game now and shoot some bullets:

Your spaceship can finally shoot! However, the bullets don’t leave the screen, which might be an issue.
Wrapping the Bullets
At the moment, all game objects are wrapped around the screen. That includes bullets. However, because of this wrapping, the screen quickly gets filled with bullets flying in all directions. That might make the game a bit too easy!
You can solve this issue by disabling the wrapping only for bullets. Override move() dans le Balle class in the space_rocks/models.py file like this:
def move(self, surface):
self.positionner = self.positionner + self.velocity
That way the bullets won’t wrap around the screen. However, they also won’t be destroyed. Instead, they’ll continue flying into the infinite abyss of the cosmos. Soon, your list of bullets will contain thousands of elements, and all of them will be processed in each frame, resulting in a decline of the performance of your game.
To avoid that situation, your game should remove the bullets as soon as they leave the screen. Update the _process_game_logic() method of the SpaceRocks class in the space_rocks/game.py file:
1def _process_game_logic(self):
2 pour game_object dans self._get_game_objects():
3 game_object.move(self.screen)
4
5 if self.spaceship:
6 pour astéroïde dans self.astéroïdes:
7 if astéroïde.collides_with(self.spaceship):
8 self.spaceship = Aucun
9 Pause
dix
11 pour balle dans self.bullets[:]:
12 if ne pas self.screen.get_rect().collidepoint(balle.positionner):
13 self.bullets.remove(balle)
Notice that instead of using the original list, self.bullets, you create a copy of it using self.bullets[:] in line 11. That’s because removing elements from a list while iterating over it can cause errors.
Surfaces in Pygame have a get_rect() method that returns a rectangle representing their area. That rectangle, in turn, has a collidepoint() method that returns True if a point is included in the rectangle and False otherwise. Using these two methods, you can check if the bullet has left the screen, and if so, remove it from the list.
Colliding With Asteroids
A crucial element of your bullets is still missing: the ability to destroy asteroids! You’ll fix that in this section.
Update the _process_game_logic() method of the SpaceRocks class like this:
1def _process_game_logic(self):
2 pour game_object dans self._get_game_objects():
3 game_object.move(self.screen)
4
5 if self.spaceship:
6 pour astéroïde dans self.astéroïdes:
7 if astéroïde.collides_with(self.spaceship):
8 self.spaceship = Aucun
9 Pause
dix
11 pour balle dans self.bullets[:]:
12 pour astéroïde dans self.astéroïdes[:]:
13 if astéroïde.collides_with(balle):
14 self.astéroïdes.remove(astéroïde)
15 self.bullets.remove(balle)
16 Pause
17
18 pour balle dans self.bullets[:]:
19 if ne pas self.screen.get_rect().collidepoint(balle.positionner):
20 self.bullets.remove(balle)
Now, whenever a collision is detected between a bullet and an asteroid, both will be removed from the game. Notice that, just like before in the bullet loop, you don’t use the original lists here. Instead, you create copies using [:] in lines 11 and 12.
If you run your game now and take a good aim when shooting, then you should be able to destroy some asteroids:

Your spaceship can finally protect itself! However, there are only six big targets in the game. Next, you’ll make it a bit more challenging.
Step 8: Splitting the Asteroids
At this point, you have a game with a spaceship, asteroids, and bullets. At the end of this step, your asteroids will split when hit by a bullet. A big asteroid will turn into two medium ones, a medium one will turn into two small ones, and a small one will disappear.
The size of an asteroid will be represented by a number:
| Asteroid size | Asteroid type |
|---|---|
3 |
Big asteroid |
2 |
Medium asteroid |
1 |
Small asteroid |
Each time an asteroid is hit, it will produce two asteroids with a smaller size. The exception is an asteroid with a size 1, as it should not produce any new asteroids.
The size of an asteroid will also determine the size of its sprite, and consequently its radius. In other words, the asteroids will be scaled like this:
| Asteroid size | Asteroid scale | Description |
|---|---|---|
| 3 | 1 | The default sprite and radius |
| 2 | 0,5 | Half the default sprite and radius |
| 1 | 0,25 | One-quarter of the default sprite and radius |
This might seem a bit complicated, but you can do it with just a few lines of code. Rewrite the constructor of the Astéroïde class in the space_rocks/models.py file:
def __init__(self, positionner, size=3):
self.size = size
size_to_scale =
3: 1,
2: 0,5,
1: 0,25,
escalader = size_to_scale[[[[size]
sprite = rotozoom(load_sprite("asteroid"), 0, escalader)
super().__init__(
positionner, sprite, get_random_velocity(1, 3)
)
This method will assign a size to an asteroid, using the default value 3, which represents a big asteroid. It will also scale the original sprite by using rotozoom(). You’ve used it before for rotating the spaceship. This method can also be used for scaling if the angle is 0 and the scale is anything other than 0. In this example, the size_to_scale lookup table contains scales for different sizes:
| Taille | Escalader |
|---|---|
| 3 | 1 |
| 2 | 0,5 |
| 1 | 0,25 |
Finally, you pass the scaled sprite to the constructor of the GameObject class, which will take care of calculating the radius based on the new image size.
Your new logic requires an asteroid to be able to create new asteroids. The situation is similar to the spaceship and bullets, so you can use a similar solution: a callback method.
Update the constructor of the Astéroïde class again:
def __init__(self, positionner, create_asteroid_callback, size=3):
self.create_asteroid_callback = create_asteroid_callback
self.size = size
size_to_scale =
3: 1,
2: 0,5,
1: 0,25,
escalader = size_to_scale[[[[size]
sprite = rotozoom(load_sprite("asteroid"), 0, escalader)
super().__init__(
positionner, sprite, get_random_velocity(1, 3)
)
Now you can create a method called split() in the same class:
def diviser(self):
if self.size > 1:
pour _ dans gamme(2):
astéroïde = Astéroïde(
self.positionner, self.create_asteroid_callback, self.size - 1
)
self.create_asteroid_callback(astéroïde)
This will create two new asteroids at the same position as the current one. Each of them will have a slightly smaller size. This logic will happen only if the current asteroid is a medium or large one.
Now you can add the callback to each newly created asteroid in the constructor of the SpaceRocks class. Just like in the case of the spaceship, you’ll use the append() method of the proper list:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.Contexte = load_sprite("space", False)
self.l'horloge = pygame.time.Clock()
self.astéroïdes = []
self.bullets = []
self.spaceship = Spaceship((400, 300), self.bullets.append)
pour _ dans gamme(6):
while True:
positionner = get_random_position(self.screen)
if (
positionner.distance_to(self.spaceship.positionner)
> self.MIN_ASTEROID_DISTANCE
):
Pause
self.astéroïdes.append(Astéroïde(positionner, self.astéroïdes.append))
Remember to call split() when an asteroid gets hit by a bullet. Update the _process_game_logic() method of the SpaceRocks class:
def _process_game_logic(self):
pour game_object dans self._get_game_objects():
game_object.move(self.screen)
if self.spaceship:
pour astéroïde dans self.astéroïdes:
if astéroïde.collides_with(self.spaceship):
self.spaceship = Aucun
Pause
pour balle dans self.bullets[:]:
pour astéroïde dans self.astéroïdes[:]:
if astéroïde.collides_with(balle):
self.astéroïdes.remove(astéroïde)
self.bullets.remove(balle)
astéroïde.diviser()
Pause
pour balle dans self.bullets[:]:
if ne pas self.screen.get_rect().collidepoint(balle.positionner):
self.bullets.remove(balle)
If you run your game now and shoot down some asteroids, then you’ll notice that, instead of disappearing right away, they split into smaller ones:

You just implemented the entire logic of the game! The spaceship can move, it gets destroyed after colliding with an asteroid, it shoots bullets, and asteroids split into smaller ones. But the game is silent at the moment. You’ll take care of that next.
Step 9: Playing Sounds
At this point, your program displays all the game objects and handles interactions between them. At the end of this step, your game will also play sounds.
In Step 7, the spaceship was equipped with a weapon. That weapon is, however, completely silent. This is very accurate in terms of physics, since sounds don’t travel in a vacuum (“In space no one can hear you scream”). Nevertheless, using sounds in your game would make it much more attractive.
First, create an assets/sounds directory and add the laser sound there. You can download the source code by clicking the link below:
Your project’s structure should look like this:
awesome_pygame_project/
|
├── assets/
| |
│ ├── sounds/
│ │ └── laser.wav
| |
│ └── sprites/
│ ├── asteroid.png
│ ├── bullet.png
│ ├── space.png
│ └── spaceship.png
|
├── space_rocks/
│ ├── __main__.py
│ ├── game.py
│ ├── models.py
│ └── utils.py
|
└── requirements.txt
Now you need to load the file. In Pygame, a sound is represented by the Sound class from the pygame.mixer module. Although you will only use a single sound in this game, you might want to add more later. That’s why you’ll create a helper method for loading sounds, similar to the one you created for sprites.
First, import the Sound class in the space_rocks/utils.py file:
import random
de pygame.image import load
de pygame.math import Vector2
de pygame.mixer import Sound
Next, create a method called load_sound() in the same file:
def load_sound(Nom):
path = F"assets/sounds/Nom.wav"
revenir Sound(path)
The method has a similar logic to load_sprite(). It will assume that the sound is always located in the assets/sounds directory and that it’s a WAV file.
You can now import this new method in the space_rocks/models.py file:
de pygame.math import Vector2
de pygame.transform import rotozoom
de utils import get_random_velocity, load_sound, load_sprite, wrap_position
Then load the sound in the constructor of the Spaceship class:
def __init__(self, positionner, create_bullet_callback):
self.create_bullet_callback = create_bullet_callback
self.laser_sound = load_sound("laser")
# Make a copy of the original UP vector
self.direction = Vector2(EN HAUT)
super().__init__(positionner, load_sprite("spaceship"), Vector2(0))
Finally, you should play the sound whenever the spaceship shoots. Update shoot():
def shoot(self):
bullet_velocity = self.direction * self.BULLET_SPEED + self.velocity
balle = Balle(self.positionner, bullet_velocity)
self.create_bullet_callback(balle)
self.laser_sound.jouer()
Run the game now and you’ll hear a sound every time you shoot.
You’ve just learned how to work with audio files in Pygame! All that’s left is displaying a message at the end of the game.
Step 10: Ending the Game
At this point, your game is almost complete, with input handling, interactions, images, and even sounds. At the end of this step, you’ll also display the status of the game on the screen.
Many games show some additional information, both during the game and after it’s over. This can be a number of remaining hit points, a shield level, an ammo count, a total score for the mission, and so on. In this game, you’ll display the status of the game.
If the spaceship is destroyed by an asteroid, then the message You lost! should appear on the screen. But if all the asteroids are gone and the spaceship is still there, then you should display You won!
Pygame doesn’t have any advanced tools for drawing text, which means more work for the programmer. Rendered text is represented by a surface with a transparent background. You can manipulate that surface the same way you do with sprites, for example by using blit(). The surface itself is created using a font.
The full process of working with text in Pygame looks like this:
-
Create a font: The font is represented by the
pygame.font.Fontclass. You can use a custom font file, or you can use the default font. For this game, you’ll do the latter. -
Create a surface with the text: This is done using
Font.render(). You’ll learn more about that method later in this tutorial. For now, it’s enough to know that it creates a surface with the rendered text and a transparent background. -
Blit the surface onto the screen: As with any other surface in Pygame, the text will only be visible if you blit it onto the screen or another surface that will eventually be shown on the screen.
Your font will be rendered with a color. In Step 1, you created a color using three values: red, green, and blue. In this section, you’ll use a Couleur class instead. Start by importing it into the space_rocks/utils.py file:
import random
de pygame import Couleur
de pygame.image import load
de pygame.math import Vector2
de pygame.mixer import Sound
Then, create a print_text() method in the same file:
1def print_text(surface, text, Police de caractère, Couleur=Couleur("tomato")):
2 text_surface = Police de caractère.render(text, True, Couleur)
3
4 rect = text_surface.get_rect()
5 rect.centre = Vector2(surface.get_size()) / 2
6
7 surface.blit(text_surface, rect)
Here’s what’s happening:
-
Ligne 1 is the declaration of your method. It takes a surface to render the text on, the text itself, a font, and a color. Le
Couleurclass offers a lot of predefined colors, which you can find in the Pygame repository. Your method will use a default color called"tomato". -
Ligne 2 creates the surface with the text using
render(). Its first argument is the text that needs to be rendered. The second is an antialiasing flag. Setting it toTruewill smooth out the edges of the rendered text. The last argument is the color of the text. -
Ligne 4 obtains a rectangle that represents the area of the surface with your text. That rectangle is an instance of the
Rectclass and can be easily moved and aligned. You can read more about aligning in the documentation. -
Ligne 5 sets the
centreattribute of the rectangle to a point in the middle of the screen. That point is calculated by dividing the screen’s size by2. This operation ensures that your text will be displayed in the center of the screen. -
Ligne 7 draws the text on the screen. Notice that this time, you pass a rectangle, not a point, to
blit(). In this case, the method will take the top-left corner of the given rectangle and will start the blitting process there.
Now you can import this method in the space_rocks/game.py file:
import pygame
de models import Astéroïde, Spaceship
de utils import get_random_position, load_sprite, print_text
Now you need to create a font. You should also store the message that will be displayed. Edit the constructor of the SpaceRocks class:
def __init__(self):
self._init_pygame()
self.screen = pygame.display.set_mode((800, 600))
self.Contexte = load_sprite("space", False)
self.l'horloge = pygame.time.Clock()
self.Police de caractère = pygame.Police de caractère.Font(Aucun, 64)
self.message = ""
self.astéroïdes = []
self.bullets = []
self.spaceship = Spaceship((400, 300), self.bullets.append)
pour _ dans gamme(6):
while True:
positionner = get_random_position(self.screen)
if (
positionner.distance_to(self.spaceship.positionner)
> self.MIN_ASTEROID_DISTANCE
):
Pause
self.astéroïdes.append(Astéroïde(positionner, self.astéroïdes.append))
The constructor of the Font class takes two arguments:
- The name of the font file, where
Aucunmeans that a default font will be used - The size of the font in pixels
The content of the message needs to be set properly. When the spaceship is destroyed, set it to "You lost!". When all the asteroids are destroyed, set it to "You won!". Edit the _process_game_logic() method of the SpaceRocks class:
def _process_game_logic(self):
pour game_object dans self._get_game_objects():
game_object.move(self.screen)
if self.spaceship:
pour astéroïde dans self.astéroïdes:
if astéroïde.collides_with(self.spaceship):
self.spaceship = Aucun
self.message = "You lost!"
Pause
pour balle dans self.bullets[:]:
pour astéroïde dans self.astéroïdes[:]:
if astéroïde.collides_with(balle):
self.astéroïdes.remove(astéroïde)
self.bullets.remove(balle)
astéroïde.diviser()
Pause
pour balle dans self.bullets[:]:
if ne pas self.screen.get_rect().collidepoint(balle.positionner):
self.bullets.remove(balle)
if ne pas self.astéroïdes et self.spaceship:
self.message = "You won!"
The last thing you need to do is actually display the message on the screen. Update the _draw() method of the SpaceRocks class:
def _draw(self):
self.screen.blit(self.Contexte, (0, 0))
pour game_object dans self._get_game_objects():
game_object.tirer(self.screen)
if self.message:
print_text(self.screen, self.message, self.Police de caractère)
pygame.display.retourner()
self.l'horloge.cocher(60)
Go ahead and test it. Start the game and crash the spaceship into an asteroid:

The game correctly shows a the message You lost!.
Now put some more effort and try to destroy all the asteroids. If you manage to do this, then you should see a victory screen:

In this step, you’ve learned how to display a text message on the screen. That was the last step of this tutorial. Your game is now complete!
Conclusion
Congratulations, you just built a clone of the Asteroids game using Python! With Pygame, your Python knowledge can be directly translated into game development projects.
In this tutorial, you’ve learned how to:
- Load images and display them on the screen
- Add input handling to your game
- Implement game logic et collision detection in Python
- Play sounds
- Afficher text on the screen
You went through the entire process of designing a game, structuring files, importing and using assets, and coding the logic. You can use all that knowledge for all your amazing future projects!
Click the link below to download the code for this project and follow along as you build your game:
Next Steps
Your Asteroids game in Python is complete, but there are so many features that you can add. Here are a few ideas to get you started:
- Restrict the maximum speed of the spaceship.
- Play a sound when an asteroid is destroyed.
- Add a shield to the spaceship, allowing it to survive a single collision.
- Keep a record of the top scores.
- Make the bullets destroy the spaceship too and wrap them around the screen, making the game much more difficult!
What other ideas can you come up with to extend this project? Be creative and have fun! In this case, as they say, space is the limit 😃
If you’re interested in learning more about game development in Python, then here are some additional resources:
[ad_2]