Introduction à la programmation de jeux en Python – Real Python

By | septembre 16, 2019

python pour débutant

Lorsque j'ai commencé à apprendre la programmation informatique à la fin du dernier millénaire, cela tenait à mon désir d'écrire des jeux informatiques. J'ai essayé de comprendre comment écrire des jeux dans toutes les langues et sur toutes les plateformes que j'ai apprises, y compris Python. C’est comme ça que j’ai découvert pygame et appris à l'utiliser pour écrire des jeux et d'autres programmes graphiques. À l'époque, je voulais vraiment un livre d'introduction sur pygame.

À la fin de cet article, vous pourrez:

  • Dessiner des objets sur votre écran
  • Jouer des effets sonores et de la musique
  • Gérer les entrées de l'utilisateur
  • Implémenter des boucles d'événements
  • Décrire en quoi la programmation de jeux diffère de la programmation Python procédurale standard

Cette introduction suppose que vous maîtrisez les bases de l’écriture de programmes Python, y compris les fonctions définies par l’utilisateur, les importations, les boucles et les conditions. Vous devez également savoir comment ouvrir des fichiers sur votre plate-forme. Une compréhension de base de Python orienté objet est également utile. pygame fonctionne avec la plupart des versions de Python, mais Python 3.6 est recommandé et utilisé tout au long de cet article.

Vous pouvez obtenir tout le code dans cet article à suivre:

Contexte et configuration

pygame est un wrapper Python pour la bibliothèque SDL, qui signifie Couche DirectMedia simple. SDL fournit un accès multiplate-forme aux composants matériels multimédia sous-jacents de votre système, tels que le son, la vidéo, la souris, le clavier et la manette de jeu. pygame a commencé sa vie en remplacement du projet bloqué PySDL. La nature multiplate-forme de SDL et de pygame signifie que vous pouvez écrire des jeux et des programmes multimédia riches en Python pour chaque plate-forme qui les prend en charge!

À installer pygame sur votre plate-forme, utilisez le pépin commander:

Vous pouvez vérifier l’installation en chargeant l’un des exemples fournis avec la bibliothèque:

$ python3 -m pygame.examples.aliens

Si une fenêtre de jeu apparaît, alors pygame est installé correctement! Si vous rencontrez des problèmes, le guide de mise en route décrit certains problèmes connus et certaines mises en garde concernant toutes les plateformes.

Programme PyGame de base

Avant de passer aux détails, jetons un coup d’œil à un pygame programme. Ce programme crée une fenêtre, remplit l’arrière-plan de blanc et trace un cercle bleu au milieu:

    1 # Programme simple pygame
    2 
    3 # Importer et initialiser la bibliothèque pygame
    4 importation pygame
    5 pygame.init()
    6 
    sept # Configurer la fenêtre de dessin
    8 écran = pygame.afficher.mode réglages([[[[500, 500])
    9 
dix # Exécuter jusqu'à ce que l'utilisateur demande à quitter
11 fonctionnement = Vrai
12 tandis que fonctionnement:
13 
14     # L'utilisateur at-il cliqué sur le bouton de fermeture de la fenêtre?
15     pour un événement dans pygame.un événement.obtenir():
16         si un événement.type == pygame.QUITTER:
17             fonctionnement = Faux
18 
19     # Remplir le fond avec du blanc
20     écran.remplir((255, 255, 255))
21 
22     # Dessine un cercle bleu uni au centre
23     pygame.dessiner.cercle(écran, (0, 0, 255), (250, 250), 75)
24 
25     # Retournez l'écran
26     pygame.afficher.retourner()
27 
28 # Terminé! Il est temps d'arrêter.
29 pygame.quitter()

Lorsque vous exécutez ce programme, vous verrez une fenêtre qui ressemble à ceci:

Un simple programme pygame

Découpons ce code section par section:

  • Lignes 4 et 5 importer et initialiser le pygame bibliothèque. Sans ces lignes, il n'y a pas de pygame.

  • Ligne 8 configure la fenêtre d’affichage de votre programme. Vous fournissez une liste ou un tuple spécifiant la largeur et la hauteur de la fenêtre à créer. Ce programme utilise une liste pour créer une fenêtre carrée de 500 pixels de chaque côté.

  • Lignes 11 et 12 mettre en place un boucle de jeu pour contrôler quand le programme se termine. Vous couvrirez les boucles de jeu plus tard dans ce tutoriel.

  • Lignes 15 à 17 numériser et manipuler événements dans la boucle de jeu. Vous pourrez aussi assister aux événements un peu plus tard. Dans ce cas, le seul événement traité est pygame.QUIT, qui se produit lorsque l’utilisateur clique sur le bouton de fermeture de la fenêtre.

  • Ligne 20 remplit la fenêtre d'une couleur unie. screen.fill () accepte une liste ou un tuple spécifiant les valeurs RVB de la couleur. Puisque (255, 255, 255) a été fourni, la fenêtre est remplie de blanc.

  • Ligne 23 dessine un cercle dans la fenêtre en utilisant les paramètres suivants:

    • écran: la fenêtre sur laquelle dessiner
    • (0, 0, 255): un tuple contenant des valeurs de couleur RVB
    • (250, 250): un tuple spécifiant les coordonnées du centre du cercle
    • 75: le rayon du cercle à dessiner en pixels
  • Ligne 26 met à jour le contenu de l'affichage à l'écran. Sans cet appel, rien ne s'affiche dans la fenêtre!

  • Ligne 29 sorties pygame. Cela ne se produit qu'une fois la boucle terminée.

C'est le pygame version de «Hello, World». Maintenant, approfondissons un peu les concepts derrière ce code.

Concepts PyGame

Comme pygame et la bibliothèque SDL sont portables sur différentes plates-formes et périphériques. Elles doivent toutes deux définir et utiliser des abstractions pour différentes réalités matérielles. Comprendre ces concepts et ces abstractions vous aidera à concevoir et à développer vos propres jeux.

Initialisation et Modules

le pygame La bibliothèque est composée d’un certain nombre de constructions Python, qui incluent plusieurs modules. Ces modules fournissent un accès abstrait à un matériel spécifique de votre système, ainsi que des méthodes uniformes d'utilisation de ce matériel. Par exemple, afficher permet un accès uniforme à votre affichage vidéo, tout en manette permet un contrôle abstrait de votre joystick.

Après avoir importé le pygame bibliothèque dans l'exemple ci-dessus, la première chose que vous avez faite a été d'initialiser PyGame en utilisant pygame.init (). Cette fonction appelle le séparé init () fonctions de tous les inclus pygame modules. Étant donné que ces modules sont des abstractions pour du matériel spécifique, cette étape d'initialisation est nécessaire pour que vous puissiez utiliser le même code sous Linux, Windows et Mac.

Affichages et surfaces

En plus des modules, pygame comprend également plusieurs Python Des classes, qui encapsule des concepts non dépendants du matériel. L'un d'entre eux est le Surface qui, à la base, définit une zone rectangulaire sur laquelle vous pouvez dessiner. Surface les objets sont utilisés dans de nombreux contextes pygame. Plus tard, vous verrez comment charger une image dans un Surface et l'afficher à l'écran.

Dans pygame, tout est visualisé sur un seul utilisateur créé afficher, qui peut être une fenêtre ou un plein écran. L’affichage est créé avec .mode réglages(), qui retourne un Surface représentant la partie visible de la fenêtre. C'est ça Surface que vous passez dans des fonctions de dessin comme pygame.draw.circle ()et le contenu de cette Surface sont poussés à l'écran lorsque vous appelez pygame.display.flip ().

Images et Rects

Votre base pygame programme a dessiné une forme directement sur le Surface, mais vous pouvez également utiliser des images sur le disque. le image Ce module vous permet de charger et d’enregistrer des images dans divers formats populaires. Les images sont chargées dans Surface objets, qui peuvent ensuite être manipulés et affichés de nombreuses façons.

Comme mentionné ci-dessus, Surface les objets sont représentés par des rectangles, comme de nombreux autres objets pygame, tels que des images et des fenêtres. Les rectangles sont tellement utilisés qu’il existe une spéciale Rect classe juste pour les manipuler. Vous utiliserez Rect des objets et des images dans votre jeu pour attirer les joueurs et les ennemis, et pour gérer les collisions entre eux.

Ok, c’est assez de théorie. Créons et écrivons un jeu!

Conception du jeu de base

Avant de commencer à écrire du code, il est toujours judicieux d’avoir une conception en place. Comme il s’agit d’un didacticiel, concevons également un gameplay de base:

  • Le but du jeu est d'éviter les obstacles entrants:
    • Le joueur commence sur le côté gauche de l'écran.
    • Les obstacles entrent au hasard de la droite et se déplacent à gauche en ligne droite.
  • Le joueur peut se déplacer à gauche, à droite, en haut ou en bas pour éviter les obstacles.
  • Le joueur ne peut pas quitter l'écran.
  • Le jeu se termine lorsque le joueur est heurté par un obstacle ou lorsque l'utilisateur ferme la fenêtre.

Quand il décrivait des projets logiciels, un ancien de mes collègues disait: «Vous ne savez pas ce que vous faites avant de savoir ce que vous ne faites pas.» Dans cet esprit, voici certaines choses qui ne seront pas couvert dans ce tutoriel:

  • Pas de vies multiples
  • Pas de notation
  • Aucune capacité d'attaque de joueur
  • Aucun niveau avancé
  • Pas de boss

Vous êtes libre d'essayer d'ajouter ces fonctionnalités, ainsi que d'autres, à votre propre programme.

Commençons!

Importation et initialisation de PyGame

Après avoir importé pygame, vous devrez également l’initialiser. Ceci permet pygame pour connecter ses abstractions à votre matériel spécifique:

    1 # Importer le module pygame
    2 importation pygame
    3 
    4 # Importez pygame.locals pour un accès plus facile aux coordonnées de clé
    5 # Mis à jour pour se conformer aux normes flake8 et black
    6 de pygame.locals importation (
    sept     K_UP,
    8     K_DOWN,
    9     K_LEFT,
dix     K_RIGHT,
11     K_ESCAPE,
12     TOUCHE BAS,
13     QUITTER,
14 )
15 
16 # Initialize pygame
17 pygame.init()

le pygame La bibliothèque définit beaucoup de choses en plus des modules et des classes. Il définit également certaines constantes locales pour des éléments tels que les frappes au clavier, les mouvements de la souris et les attributs d'affichage. Vous référencez ces constantes en utilisant la syntaxe Pygame.. En important des constantes spécifiques de pygame.locals, vous pouvez utiliser la syntaxe au lieu. Cela vous évitera des frappes au clavier et améliorera la lisibilité globale.

Configuration de l'affichage

Maintenant vous avez besoin de quelque chose Créez un écran pour qu'il soit la toile globale:

    1 # Importer le module pygame
    2 importation pygame
    3 
    4 # Importez pygame.locals pour un accès plus facile aux coordonnées de clé
    5 # Mis à jour pour se conformer aux normes flake8 et black
    6 de pygame.locals importation (
    sept     K_UP,
    8     K_DOWN,
    9     K_LEFT,
dix     K_RIGHT,
11     K_ESCAPE,
12     TOUCHE BAS,
13     QUITTER,
14 )
15 
16 # Initialize pygame
17 pygame.init()
18 
19 # Définir des constantes pour la largeur et la hauteur de l'écran
20 SCREEN_WIDTH = 800
21 SCREEN_HEIGHT = 600
22 
23 # Créer l'objet d'écran
24 # La taille est déterminée par les constantes SCREEN_WIDTH et SCREEN_HEIGHT.
25 écran = pygame.afficher.mode réglages((SCREEN_WIDTH, SCREEN_HEIGHT))

Vous créez l'écran à utiliser en appelant pygame.display.set_mode () et passer un tuple ou une liste avec la largeur et la hauteur désirées. Dans ce cas, la fenêtre est 800×600, telle que définie par les constantes SCREEN_WIDTH et SCREEN_HEIGHT sur les lignes 20 et 21. Ceci retourne un Surface qui représente les dimensions intérieures de la fenêtre. C'est la partie de la fenêtre que vous pouvez contrôler, tandis que le système d'exploitation contrôle les bordures de la fenêtre et la barre de titre.

Si vous exécutez ce programme maintenant, une fenêtre s’ouvrira brièvement, puis disparaîtra immédiatement à la fermeture du programme. Ne clignez pas des yeux ou vous risquez de le manquer! Dans la section suivante, vous allez vous concentrer sur la boucle de jeu principale pour vous assurer que votre programme ne se ferme que lorsque la saisie est correcte.

Mise en place de la boucle de jeu

Chaque jeu, de Pong à Fortnite, utilise une boucle de jeu pour contrôler le jeu. La boucle de jeu fait quatre choses très importantes:

  1. Traite la saisie de l'utilisateur
  2. Met à jour l'état de tous les objets du jeu
  3. Met à jour l'affichage et la sortie audio
  4. Maintient la vitesse du jeu

Chaque cycle de la boucle de jeu est appelé un Cadre, et plus vite vous pourrez faire les choses à chaque cycle, plus votre jeu sera rapide. Les cadres continuent de se produire jusqu'à ce qu'une condition de sortie du jeu soit remplie. Dans votre conception, deux conditions peuvent terminer la boucle de jeu:

  1. Le joueur se heurte à un obstacle. (Vous couvrirez la détection de collision plus tard.)
  2. Le joueur ferme la fenêtre.

La première chose que fait la boucle de jeu est de traiter les entrées de l’utilisateur pour permettre au joueur de se déplacer sur l’écran. Par conséquent, vous avez besoin d’un moyen de capturer et de traiter une variété d’entrées. Vous faites cela en utilisant le pygame système d'événements.

Événements de traitement

Les pressions sur les touches, les mouvements de la souris et même les mouvements du joystick sont quelques-unes des façons dont un utilisateur peut fournir des informations. Toutes les entrées utilisateur ont pour résultat la génération d’un événement. Les événements peuvent survenir à tout moment et ont souvent (mais pas toujours) leur origine en dehors du programme. Tous les événements dans pygame sont placés dans la file d’événements, qui peut ensuite être consultée et manipulée. Traiter des événements est appelé manipulation eux, et le code pour le faire est appelé un gestionnaire d'événements.

Chaque événement dans pygame a un événement type associé avec. Pour votre jeu, les types d’événements sur lesquels vous allez vous concentrer sont les appuis sur les touches et la fermeture des fenêtres. Les événements Keypress ont le type d'événement TOUCHE BAS, et l'événement de fermeture de la fenêtre a le type QUITTER. Différents types d'événements peuvent également être associés à d'autres données. Par exemple, le TOUCHE BAS type d'événement a également une variable appelée clé pour indiquer quelle touche a été appuyée.

Vous accédez à la liste de tous les événements actifs de la file d'attente en appelant pygame.event.get (). Vous parcourez ensuite cette liste, inspectez chaque type d’événement et répondez en conséquence:

27 # Variable pour maintenir la boucle principale active
28 fonctionnement = Vrai
29 
30 # Boucle principale
31 tandis que fonctionnement:
32     # Regardez chaque événement dans la file d'attente
33     pour un événement dans pygame.un événement.obtenir():
34         # L'utilisateur a-t-il appuyé sur une touche?
35         si un événement.type == TOUCHE BAS:
36             # Était-ce la touche d'échappement? Si oui, arrêtez la boucle.
37             si un événement.clé == K_ESCAPE:
38                 fonctionnement = Faux
39 
40         # L'utilisateur at-il cliqué sur le bouton de fermeture de la fenêtre? Si oui, arrêtez la boucle.
41         elif un événement.type == QUITTER:
42             fonctionnement = Faux

Examinons de plus près cette boucle de jeu:

  • Ligne 28 configure une variable de contrôle pour la boucle de jeu. Pour sortir de la boucle et du jeu, vous définissez en cours d'exécution = False. La boucle de jeu commence à la ligne 29.

  • Ligne 31 démarre le gestionnaire d'événements en parcourant tous les événements actuellement dans la file d'attente. En l’absence d’événements, la liste est vide et le gestionnaire ne fait rien.

  • Lignes 35 à 38 vérifier si le courant type d'événement est un TOUCHE BAS un événement. Si tel est le cas, le programme vérifie quelle touche a été actionnée en regardant le event.key attribut. Si la clé est la Esc touche, indiquée par K_ESCAPEpuis quitte la boucle de jeu en réglant en cours d'exécution = False.

  • Lignes 41 et 42 faire une vérification similaire pour le type d'événement appelé QUITTER. Cet événement ne se produit que lorsque l'utilisateur clique sur le bouton de fermeture de la fenêtre. L'utilisateur peut également utiliser toute autre action du système d'exploitation pour fermer la fenêtre.

Lorsque vous ajoutez ces lignes au code précédent et que vous l'exécutez, une fenêtre apparaît avec un écran vide ou noir:

Une fenêtre pygame vide, mais persistante

La fenêtre ne disparaît que lorsque vous appuyez sur le bouton Esc clé, ou autrement déclencher une QUITTER événement en fermant la fenêtre.

Dessin sur l'écran

Dans l'exemple de programme, vous avez dessiné sur l'écran à l'aide de deux commandes:

  1. screen.fill () pour remplir le fond
  2. pygame.draw.circle () dessiner un cercle

Vous allez maintenant découvrir une troisième façon de dessiner à l’écran: utiliser un Surface.

Rappelons qu'un Surface est un objet rectangulaire sur lequel vous pouvez dessiner, comme une feuille de papier vierge. le écran objet est un Surface, et vous pouvez créer votre propre Surface objets séparés de l'écran d'affichage. Voyons comment cela fonctionne:

44 # Remplir l'écran avec du blanc
45 écran.remplir((255, 255, 255))
46 
47 # Créer une surface et passer dans un tuple contenant sa longueur et sa largeur
48 le surf = pygame.Surface((50, 50))
49 
50 # Donne une couleur à la surface pour la séparer de l'arrière-plan
51 le surf.remplir((0, 0, 0))
52 rect = le surf.get_rect()

Une fois l’écran blanc rempli à la ligne 45, un nouveau Surface est créé à la ligne 48. Cette Surface 50 pixels de large, 50 pixels de haut, et affecté à le surf. À ce stade, vous le traitez comme le écran. Alors en ligne, vous le remplissez de noir. Vous pouvez également accéder à son sous-jacent Rect en utilisant .get_rect (). Ceci est stocké comme rect pour une utilisation ultérieure.

En utilisant .blit () et .flip ()

Je viens de créer un nouveau Surface n’est pas suffisant pour le voir à l’écran. Pour ce faire, vous devez éviter les Surface sur un autre Surface. Le terme blit représente Bloc de transfert, et .blit () comment vous copiez le contenu d’un Surface à un autre. Vous pouvez seulement .blit () D'un Surface à un autre, mais puisque l'écran n'est qu'un autre Surface, ce n'est pas un problème. Voici comment vous dessinez le surf sur l'écran:

54 # Cette ligne dit "Dessine le surf sur l'écran au centre"
55 écran.blit(le surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
56 pygame.afficher.retourner()

le .blit () L’appel à la ligne 55 prend deux arguments:

  1. le Surface dessiner
  2. L'emplacement où le dessiner sur la source Surface

Les coordonnées (SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2) dire à votre programme de placer le surf exactement au centre de l’écran, mais cela ne ressemble pas vraiment à ça:

Blitting une surface sur l'écran

La raison pour laquelle l’image est décentrée, c’est que .blit () met le coin supérieur gauche de le surf à l'emplacement indiqué. Si tu veux le surf pour être centré, vous devrez faire des calculs pour le déplacer vers le haut et vers la gauche. Vous pouvez le faire en soustrayant la largeur et la hauteur de le surf à partir de la largeur et de la hauteur de l'écran, en les divisant par 2 pour localiser le centre, puis en transmettant ces nombres comme arguments à screen.blit ():

54 # Placez le centre de surf au centre de l'écran
55 surf_center = (
56     (SCREEN_WIDTH-le surf.get_width())/2,
57     (SCREEN_HEIGHT-le surf.get_height())/2
58 )
59 
60 # Dessine le surf aux nouvelles coordonnées
61 écran.blit(le surf, surf_center)
62 pygame.afficher.retourner()

Remarquez l'appel à pygame.display.flip () après l'appel à blit (). Ceci met à jour l’ensemble de l’écran avec tout ce qui a été dessiné depuis le dernier retournement. Sans l'appel à .flip (), rien n'est montré.

Sprites

Dans votre conception de jeu, le joueur commence à gauche et les obstacles arrivent par la droite. Vous pouvez représenter tous les obstacles avec Surface objets pour faciliter le dessin, mais comment savoir où les dessiner? Comment savoir si un obstacle est entré en collision avec le joueur? Que se passe-t-il lorsque l'obstacle s'envole de l'écran? Et si vous voulez dessiner des images d’arrière-plan qui bougent aussi? Et si vous voulez que vos images soient animées? Vous pouvez gérer toutes ces situations et plus avec des sprites.

En termes de programmation, un lutin est une représentation 2D de quelque chose à l'écran. En gros, c’est une image. pygame fournit un Lutin classe, conçue pour contenir une ou plusieurs représentations graphiques de tout objet du jeu que vous souhaitez afficher à l'écran. Pour l'utiliser, vous créez une nouvelle classe qui s'étend Lutin. Cela vous permet d'utiliser ses méthodes intégrées.

Joueurs

Voici comment vous utilisez Lutin objets avec le jeu en cours pour définir le joueur. Insérer ce code après la ligne 18:

20 # Définir un objet Player en étendant pygame.sprite.Sprite
21 # La surface dessinée à l'écran est maintenant un attribut de 'joueur'
22 classe Joueur(pygame.lutin.Lutin):
23     def __init__(soi):
24         super(Joueur, soi).__init__()
25         soi.le surf = pygame.Surface((75, 25))
26         soi.le surf.remplir((255, 255, 255))
27         soi.rect = soi.le surf.get_rect()

Vous définissez d'abord Joueur en étendant pygame.sprite.Sprite à la ligne 22. Ensuite .__ init __ () les usages .super() appeler le .__ init __ () méthode de Lutin. Pour plus d'informations sur les raisons pour lesquelles cela est nécessaire, lisez Supercharge Your Classes With Python super ().

Ensuite, vous définissez et initialisez .le surf pour tenir l'image à afficher, qui est actuellement une case blanche. Vous définissez et initialisez également .rect, que vous utiliserez pour dessiner le joueur plus tard. Pour utiliser cette nouvelle classe, vous devez créer un nouvel objet et modifier également le code de dessin. Développez le bloc de code ci-dessous pour tout voir ensemble:

    1 # Importer le module pygame
    2 importation pygame
    3 
    4 # Importez pygame.locals pour un accès plus facile aux coordonnées de clé
    5 # Mis à jour pour se conformer aux normes flake8 et black
    6 de pygame.locals importation (
    sept     K_UP,
    8     K_DOWN,
    9     K_LEFT,
dix     K_RIGHT,
11     K_ESCAPE,
12     TOUCHE BAS,
13     QUITTER,
14 )
15 
16 # Définir des constantes pour la largeur et la hauteur de l'écran
17 SCREEN_WIDTH = 800
18 SCREEN_HEIGHT = 600
19 
20 # Définir un objet joueur en étendant pygame.sprite.Sprite
21 # La surface dessinée à l'écran est maintenant un attribut de 'joueur'
22 classe Joueur(pygame.lutin.Lutin):
23     def __init__(soi):
24         super(Joueur, soi).__init__()
25         soi.le surf = pygame.Surface((75, 25))
26         soi.le surf.remplir((255, 255, 255))
27         soi.rect = soi.le surf.get_rect()
28 
29 # Initialize pygame
30 pygame.init()
31 
32 # Créer l'objet d'écran
33 # La taille est déterminée par les constantes SCREEN_WIDTH et SCREEN_HEIGHT.
34 écran = pygame.afficher.mode réglages((SCREEN_WIDTH, SCREEN_HEIGHT))
35 
36 # Instancier le joueur. Pour l'instant, ce n'est qu'un rectangle.
37 joueur = Joueur()
38 
39 # Variable pour maintenir la boucle principale active
40 fonctionnement = Vrai
41 
42 # Boucle principale
43 tandis que fonctionnement:
44     # pour la boucle dans la file d'attente des événements
45     pour un événement dans pygame.un événement.obtenir():
46         # Vérifier l’événement KEYDOWN
47         si un événement.type == TOUCHE BAS:
48             # Si la touche Esc est enfoncée, quitte la boucle principale
49             si un événement.clé == K_ESCAPE:
50                 fonctionnement = Faux
51         # Vérifier l’événement QUIT. Si QUITTER, définissez running sur false.
52         elif un événement.type == QUITTER:
53             fonctionnement = Faux
54 
55     # Remplir l'écran avec du noir
56     écran.remplir((0, 0, 0))
57 
58     # Dessine le joueur sur l'écran
59     écran.blit(joueur.le surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
60 
61     # Mettre à jour l'affichage
62     pygame.afficher.retourner()

Exécutez ce code. Vous verrez un rectangle blanc à peu près au milieu de l'écran:

Joueur de base en cours de tir

Que se passerait-il si vous changiez la ligne 59 en screen.blit (player.surf, player.rect)? Essayez et voyez:

55 # Remplir l'écran avec du noir
56 écran.remplir((0, 0, 0))
57 
58 # Dessine le joueur sur l'écran
59 écran.blit(joueur.le surf, joueur.rect)
60 
61 # Mettre à jour l'affichage
62 pygame.afficher.retourner()

Quand vous passez un Rect à .blit (), il utilise les coordonnées du coin supérieur gauche pour dessiner la surface. Vous utiliserez cela plus tard pour faire bouger votre joueur!

Entrée utilisateur

Jusqu’à présent, vous avez appris à configurer pygame et dessinez des objets sur l'écran. Maintenant, le vrai plaisir commence! Vous rendrez le lecteur contrôlable à l’aide du clavier.

Plus tôt, vous avez vu ça pygame.event.get () renvoie une liste des événements dans la file d'événements que vous analysez TOUCHE BAS types d'événements. Eh bien, ce n’est pas la seule façon de lire les pressions de touche. pygame fournit également pygame.event.get_pressed (), qui retourne un dictionnaire contenant tout le courant TOUCHE BAS événements dans la file d'attente.

Mettez ceci dans votre boucle de jeu juste après la boucle de gestion d'événement. Ceci retourne un dictionnaire contenant les touches appuyées au début de chaque image:

54 # Obtenir le jeu de touches enfoncé et vérifier la saisie de l'utilisateur
55 touches pressées = pygame.clé.get_pressed()

Ensuite, vous écrivez une méthode dans Joueur d'accepter ce dictionnaire. Cela définira le comportement de l'image-objet en fonction des touches sur lesquelles vous appuyez. Voici à quoi cela pourrait ressembler:

29 # Déplace l'image-objet en fonction des touches de l'utilisateur
30 def mise à jour(soi, touches pressées):
31     si touches pressées[[[[K_UP]:
32         soi.rect.move_ip(0, -5)
33     si touches pressées[[[[K_DOWN]:
34         soi.rect.move_ip(0, 5)
35     si touches pressées[[[[K_LEFT]:
36         soi.rect.move_ip(-5, 0)
37     si touches pressées[[[[K_RIGHT]:
38         soi.rect.move_ip(5, 0)

K_UP, K_DOWN, K_LEFT, et K_RIGHT correspondent aux touches fléchées du clavier. Si l'entrée du dictionnaire pour cette clé est Vrai, cette touche est enfoncée et vous déplacez le joueur .rect dans le bon sens. Ici vous utilisez .move_ip (), Qui veut dire se déplacer en place, pour déplacer le courant Rect.

Ensuite, vous pouvez appeler .mise à jour() chaque image pour déplacer l’image-joueur en réponse aux pressions sur les touches. Ajouter cet appel juste après l'appel à .get_pressed ():

52 # Boucle principale
53 tandis que fonctionnement:
54     # pour la boucle dans la file d'attente des événements
55     pour un événement dans pygame.un événement.obtenir():
56         # Vérifier l’événement KEYDOWN
57         si un événement.type == TOUCHE BAS:
58             # Si la touche Esc est enfoncée, quitte la boucle principale
59             si un événement.clé == K_ESCAPE:
60                 fonctionnement = Faux
61         # Vérifier l’événement QUIT. Si QUITTER, définissez running sur false.
62         elif un événement.type == QUITTER:
63             fonctionnement = Faux
64 
65     # Obtenir toutes les touches actuellement appuyées
66     touches pressées = pygame.clé.get_pressed()
67 
68     # Mettre à jour le sprite du joueur en fonction des touches de l'utilisateur
69     joueur.mise à jour(touches pressées)
70 
71     # Remplir l'écran avec du noir
72     écran.remplir((0, 0, 0))

Maintenant, vous pouvez déplacer votre lecteur rectangle autour de l'écran avec les touches fléchées:

Touches clavier déplaçant un sprite dans pygame

Vous remarquerez peut-être deux petits problèmes:

  1. Le rectangle du joueur peut se déplacer très rapidement si une touche est maintenue enfoncée. Vous travaillerez sur cela plus tard.
  2. Le rectangle du joueur peut sortir de l'écran. Laissons-le résoudre maintenant.

Pour que le lecteur reste à l'écran, vous devez ajouter une logique pour détecter si le rect va sortir de l'écran. Pour ce faire, vous vérifiez si le rect les coordonnées ont dépassé les limites de l’écran. Si tel est le cas, vous demandez au programme de le déplacer vers le bord:

25 # Déplace l'image-objet en fonction des touches de l'utilisateur
26 def mise à jour(soi, touches pressées):
27     si touches pressées[[[[K_UP]:
28         soi.rect.move_ip(0, -5)
29     si touches pressées[[[[K_DOWN]:
30         soi.rect.move_ip(0, 5)
31     si touches pressées[[[[K_LEFT]:
32         soi.rect.move_ip(-5, 0)
33     si touches pressées[[[[K_RIGHT]:
34         soi.rect.move_ip(5, 0)
35 
36     # Garder le joueur sur l'écran
37     si soi.rect.la gauche < 0:
38         soi.rect.la gauche = 0
39     si soi.rect.droite > SCREEN_WIDTH:
40         soi.rect.droite = SCREEN_WIDTH
41     si soi.rect.Haut <= 0:
42         soi.rect.Haut = 0
43     si soi.rect.bas > = SCREEN_HEIGHT:
44         soi.rect.bas = SCREEN_HEIGHT

Ici, au lieu d'utiliser .bouge toi(), il vous suffit de modifier les coordonnées correspondantes de .Haut, .bas, .la gauche, ou .droite directement. Testez cela et vous constaterez que le rectangle du lecteur ne peut plus quitter l’écran.

Ajoutons maintenant quelques ennemis!

Ennemis

Qu'est-ce qu'un jeu sans ennemis? Vous utiliserez les techniques que vous avez déjà apprises pour créer une classe ennemie de base, puis vous en créerez beaucoup pour votre joueur. Tout d'abord, importez le Aléatoire bibliothèque:

    4 # Importer au hasard des nombres aléatoires
    5 importation Aléatoire

Créez ensuite une nouvelle classe de sprite appelée Ennemien suivant le même schéma que vous avez utilisé pour Joueur:

55 # Définit l'objet ennemi en étendant pygame.sprite.Sprite
56 # La surface que vous dessinez sur l'écran est maintenant un attribut d'ennemi
57 classe Ennemi(pygame.lutin.Lutin):
58     def __init__(soi):
59         super(Ennemi, soi).__init__()
60         soi.le surf = pygame.Surface((20, dix))
61         soi.le surf.remplir((255, 255, 255))
62         soi.rect = soi.le surf.get_rect(
63             centre=(
64                 Aléatoire.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
65                 Aléatoire.randint(0, SCREEN_HEIGHT),
66             )
67         )
68         soi.la vitesse = Aléatoire.randint(5, 20)
69 
70     # Déplace le sprite en fonction de la vitesse
71     # Supprime le sprite quand il passe le bord gauche de l'écran
72     def mise à jour(soi):
73         soi.rect.move_ip(-soi.la vitesse, 0)
74         si soi.rect.droite < 0:
75             soi.tuer()

Il y a quatre différences notables entre Ennemi et Joueur:

  1. Sur les lignes 62 à 67, vous mettez à jour rect être un emplacement aléatoire le long du bord droit de l'écran. Le centre du rectangle est juste en dehors de l'écran. Il est situé entre 20 et 100 pixels du bord droit et entre les bords supérieur et inférieur.

  2. On line 68, you define .speed as a random number between 5 and 20. This specifies how fast this enemy moves towards the player.

  3. On lines 73 to 76, you define .update(). It takes no arguments since enemies move automatically. Instead, .update() moves the enemy toward the left side of the screen at the .speed defined when it was created.

  4. On line 74, you check whether the enemy has moved off-screen. To make sure the Ennemi is fully off the screen and won’t just disappear while it’s still visible, you check that the right side of the .rect has gone past the left side of the screen. Once the enemy is off-screen, you call .kill() to prevent it from being processed further.

So, what does .kill() do? To figure this out, you have to know about Sprite Groups.

Sprite Groups

Another super useful class that pygame provides is the Sprite Group. This is an object that holds a group of Sprite objects. So why use it? Can’t you just track your Sprite objects in a list instead? Well, you can, but the advantage of using a Group lies in the methods it exposes. These methods help to detect whether any Ennemi has collided with the Player, which makes updates much easier.

Let’s see how to create sprite groups. You’ll create two different Group objects:

  1. La première Group will hold every Sprite in the game.
  2. La deuxième Group will hold just the Ennemi objects.

Here’s what that looks like in code:

82 # Create the 'player'
83 joueur = Player()
84 
85 # Create groups to hold enemy sprites and all sprites
86 # - enemies is used for collision detection and position updates
87 # - all_sprites is used for rendering
88 ennemis = pygame.lutin.Group()
89 all_sprites = pygame.lutin.Group()
90 all_sprites.ajouter(joueur)
91 
92 # Variable to keep the main loop running
93 fonctionnement = True

When you call .kill(), le Sprite is removed from every Group to which it belongs. This removes the references to the Sprite as well, which allows Python’s garbage collector to reclaim the memory as necessary.

Now that you have an all_sprites group, you can change how objects are drawn. Instead of calling .blit() on just Player, you can iterate over everything in all_sprites:

117 # Fill the screen with black
118 écran.remplir((0, 0, 0))
119 
120 # Draw all sprites
121 pour entité dans all_sprites:
122     écran.blit(entité.le surf, entité.rect)
123 
124 # Flip everything to the display
125 pygame.afficher.flip()

Now, anything put into all_sprites will be drawn with every frame, whether it’s an enemy or the player.

There’s just one problem… You don’t have any enemies! You could create a bunch of enemies at the beginning of the game, but the game would quickly become boring when they all left the screen a few seconds later. Instead, let’s explore how to keep a steady supply of enemies coming as the game progresses.

Custom Events

The design calls for enemies to appear at regular intervals. This means that at set intervals, you need to do two things:

  1. Create a new Ennemi.
  2. Add it to all_sprites et ennemis.

You already have code that handles random events. The event loop is designed to look for random events occurring every frame and deal with them appropriately. Luckily, pygame doesn’t restrict you to using only the event types it has defined. You can define your own events to handle as you see fit.

Let’s see how to create a custom event that’s generated every few seconds. You can create a custom event by naming it:

78 # Create the screen object
79 # The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
80 écran = pygame.afficher.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
81 
82 # Create a custom event for adding a new enemy
83 ADDENEMY = pygame.USEREVENT + 1
84 pygame.temps.set_timer(ADDENEMY, 250)
85 
86 # Instantiate player. Right now, this is just a rectangle.
87 joueur = Player()

pygame defines events internally as integers, so you need to define a new event with a unique integer. The last event pygame reserves is called USEREVENT, so defining ADDENEMY = pygame.USEREVENT + 1 on line 83 ensures it’s unique.

Next, you need to insert this new event into the event queue at regular intervals throughout the game. C’est là que le temps module comes in. Line 84 fires the new ADDENEMY event every 250 milliseconds, or four times per second. You call .set_timer() outside the game loop since you only need one timer, but it will fire throughout the entire game.

Add the code to handle your new event:

100 # Main loop
101 tandis que fonctionnement:
102     # Look at every event in the queue
103     pour un événement dans pygame.un événement.obtenir():
104         # Did the user hit a key?
105         si un événement.type == KEYDOWN:
106             # Was it the Escape key? If so, stop the loop.
107             si un événement.clé == K_ESCAPE:
108                 fonctionnement = False
109 
110         # Did the user click the window close button? If so, stop the loop.
111         elif un événement.type == QUIT:
112             fonctionnement = False
113 
114         # Add a new enemy?
115         elif un événement.type == ADDENEMY:
116             # Create the new enemy and add it to sprite groups
117             new_enemy = Ennemi()
118             ennemis.ajouter(new_enemy)
119             all_sprites.ajouter(new_enemy)
120 
121     # Get the set of keys pressed and check for user input
122     pressed_keys = pygame.clé.get_pressed()
123     joueur.mise à jour(pressed_keys)
124 
125     # Update enemy position
126     ennemis.mise à jour()

Whenever the event handler sees the new ADDENEMY event on line 115, it creates an Ennemi and adds it to ennemis et all_sprites. Since Ennemi est dans all_sprites, it will get drawn every frame. You also need to call enemies.update() on line 126, which updates everything in ennemis, to ensure they move properly:

Enemies flying by in pygame

However, that’s not the only reason there’s a group for just ennemis.

Collision Detection

Your game design calls for the game to end whenever an enemy collides with the player. Checking for collisions is a basic technique of game programming, and usually requires some non-trivial math to determine whether two sprites will overlap each other.

This is where a framework like pygame comes in handy! Writing collision detection code is tedious, but pygame has a LOT of collision detection methods available for you to use.

For this tutorial, you’ll use a method called .spritecollideany(), which is read as “sprite collide any.” This method accepts a Sprite et un Group as parameters. It looks at every object in the Group and checks if its .rect intersects with the .rect du Sprite. If so, then it returns True. Otherwise, it returns False. This is perfect for this game since you need to check if the single joueur collides with one of a Group de ennemis.

Here’s what that looks like in code:

130 # Draw all sprites
131 pour entité dans all_sprites:
132     écran.blit(entité.le surf, entité.rect)
133 
134 # Check if any enemies have collided with the player
135 si pygame.lutin.spritecollideany(joueur, ennemis):
136     # If so, then remove the player and stop the loop
137     joueur.tuer()
138     fonctionnement = False

Line 135 tests whether joueur has collided with any of the objects in ennemis. If so, then player.kill() is called to remove it from every group to which it belongs. Since the only objects being rendered are in all_sprites, le joueur will no longer be rendered. Once the player has been killed, you need to exit the game as well, so you set running = False to break out of the game loop on line 138.

At this point, you’ve got the basic elements of a game in place:

Pygame window

Now, let’s dress it up a bit, make it more playable, and add some advanced capabilities to help it stand out.

Sprite Images

Alright, you have a game, but let’s be honest… It’s kind of ugly. The player and enemies are just white blocks on a black background. That was state-of-the-art when Pong was new, but it just doesn’t cut it anymore. Let’s replace all those boring white rectangles with some cooler images that will make the game feel like an actual game.

Earlier, you learned that images on disk can be loaded into a Surface with some help from the image module. For this tutorial, we made a little jet for the player and some missiles for the enemies. You’re welcome to use this art, draw your own, or download some free game art assets to use. You can click the link below to download the art used in this tutorial:

Altering the Object Constructors

Before you use images to represent the player and enemy sprites, you need to make some changes to their constructors. The code below replaces the code used previously:

    sept # Import pygame.locals for easier access to key coordinates
    8 # Updated to conform to flake8 and black standards
    9 # from pygame.locals import *
dix de pygame.locals importation (
11     RLEACCEL,
12     K_UP,
13     K_DOWN,
14     K_LEFT,
15     K_RIGHT,
16     K_ESCAPE,
17     KEYDOWN,
18     QUIT,
19 )
20 
21 # Define constants for the screen width and height
22 SCREEN_WIDTH = 800
23 SCREEN_HEIGHT = 600
24 
25 
26 # Define the Player object by extending pygame.sprite.Sprite
27 # Instead of a surface, use an image for a better-looking sprite
28 classe Player(pygame.lutin.Sprite):
29     def __init__(soi):
30         super(Player, soi).__init__()
31         soi.image = pygame.image.charge("jet.png").convertir()
32         soi.image.set_colorkey((255, 255, 255), RLEACCEL)
33         soi.rect = soi.image.get_rect()

Let’s unpack line 31 a bit. pygame.image.load() loads an image from the disk. You pass it a path to the file. It returns a Surface, et le .convert() call optimizes the Surface, making future .blit() calls faster.

Line 32 uses .set_colorkey() to indicate the color pygame will render as transparent. In this case, you choose white, because that’s the background color of the jet image. The RLEACCEL constant is an optional parameter that helps pygame render more quickly on non-accelerated displays. This is added to the pygame.locals import statement on line 11.

Nothing else needs to change. The image is still a Surface, except now it has a picture painted on it. You still use it in the same way.

Here’s what similar changes to the Ennemi look like:

59 # Define the enemy object by extending pygame.sprite.Sprite
60 # Instead of a surface, use an image for a better-looking sprite
61 classe Ennemi(pygame.lutin.Sprite):
62     def __init__(soi):
63         super(Ennemi, soi).__init__()
64         soi.le surf = pygame.image.charge("missile.png").convertir()
65         soi.le surf.set_colorkey((255, 255, 255), RLEACCEL)
66         # The starting position is randomly generated, as is the speed
67         soi.rect = soi.le surf.get_rect(
68             centre=(
69                 Aléatoire.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
70                 Aléatoire.randint(0, SCREEN_HEIGHT),
71             )
72         )
73         soi.la vitesse = Aléatoire.randint(5, 20)

Running the program now should show that this is the same game you had before, except now you’ve added some nice graphics les peaux with images. But why stop at just making the player and enemy sprites look nice? Let’s add a few clouds going past to give the impression of a jet flying through the sky.

Adding Background Images

For background clouds, you use the same principles as you did for Player et Ennemi:

  1. Create the Cloud class.
  2. Add an image of a cloud to it.
  3. Create a method .update() that moves the nuage toward the left side of the screen.
  4. Create a custom event and handler to create new nuage objects at a set time interval.
  5. Add the newly created nuage objects to a new Group appelé des nuages.
  6. Update and draw the des nuages in your game loop.

Here’s what Cloud looks like:

    83 # Define the cloud object by extending pygame.sprite.Sprite
    84 # Use an image for a better-looking sprite
    85 classe Cloud(pygame.lutin.Sprite):
    86     def __init__(soi):
    87         super(Cloud, soi).__init__()
    88         soi.le surf = pygame.image.charge("cloud.png").convertir()
    89         soi.le surf.set_colorkey((0, 0, 0), RLEACCEL)
    90         # The starting position is randomly generated
    91         soi.rect = soi.le surf.get_rect(
    92             centre=(
    93                 Aléatoire.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
    94                 Aléatoire.randint(0, SCREEN_HEIGHT),
    95             )
    96 
    97     # Move the cloud based on a constant speed
    98     # Remove the cloud when it passes the left edge of the screen
    99     def mise à jour(soi):
100         soi.rect.move_ip(-5, 0)
101         si soi.rect.droite < 0:
102             soi.tuer()

That should all look very familiar. It’s pretty much the same as Ennemi.

To have clouds appear at certain intervals, you’ll use event creation code similar to what you used to create new enemies. Put it right below the enemy creation event:

116 # Create custom events for adding a new enemy and a cloud
117 ADDENEMY = pygame.USEREVENT + 1
118 pygame.temps.set_timer(ADDENEMY, 250)
119 ADDCLOUD = pygame.USEREVENT + 2
120 pygame.temps.set_timer(ADDCLOUD, 1000)

This says to wait 1000 milliseconds, or one second, before creating the next nuage.

Next, create a new Group to hold each newly created nuage:

125 # Create groups to hold enemy sprites, cloud sprites, and all sprites
126 # - enemies is used for collision detection and position updates
127 # - clouds is used for position updates
128 # - all_sprites is used for rendering
129 ennemis = pygame.lutin.Group()
130 des nuages = pygame.lutin.Group()
131 all_sprites = pygame.lutin.Group()
132 all_sprites.ajouter(joueur)

Next, add a handler for the new ADDCLOUD event in the event handler:

137 # Main loop
138 tandis que fonctionnement:
139     # Look at every event in the queue
140     pour un événement dans pygame.un événement.obtenir():
141         # Did the user hit a key?
142         si un événement.type == KEYDOWN:
143             # Was it the Escape key? If so, then stop the loop.
144             si un événement.clé == K_ESCAPE:
145                 fonctionnement = False
146 
147         # Did the user click the window close button? If so, stop the loop.
148         elif un événement.type == QUIT:
149             fonctionnement = False
150 
151         # Add a new enemy?
152         elif un événement.type == ADDENEMY:
153             # Create the new enemy and add it to sprite groups
154             new_enemy = Ennemi()
155             ennemis.ajouter(new_enemy)
156             all_sprites.ajouter(new_enemy)
157 
158         # Add a new cloud?
159         elif un événement.type == ADDCLOUD:
160             # Create the new cloud and add it to sprite groups
161             new_cloud = Cloud()
162             des nuages.ajouter(new_cloud)
163             all_sprites.ajouter(new_cloud)

Finally, make sure the des nuages are updated every frame:

167 # Update the position of enemies and clouds
168 ennemis.mise à jour()
169 des nuages.mise à jour()
170 
171 # Fill the screen with sky blue
172 écran.remplir((135, 206, 250))

Line 172 updates the original screen.fill() to fill the screen with a pleasant sky blue color. You can change this color to something else. Maybe you want an alien world with a purple sky, a toxic wasteland in neon green, or the surface of Mars in red!

Note that each new Cloud et Ennemi are added to all_sprites aussi bien que des nuages et ennemis. This is done because each group is used for a separate purpose:

  • Rendering is done using all_sprites.
  • Position updates are done using des nuages et ennemis.
  • Collision detection is done using ennemis.

You create multiple groups so that you can change the way sprites move or behave without impacting the movement or behavior of other sprites.

Game Speed

While testing the game you may have noticed that the enemies move a little fast. If not, then that’s okay, as different machines will see different results at this point.

The reason for this is that the game loop processes frames as fast as the processor and environment will allow. Since all the sprites move once per frame, they can move hundreds of times each second. The number of frames handled each second is called the cadence, and getting this right is the difference between a playable game and a forgettable one.

Normally, you want as high a frame rate as possible, but for this game, you need to slow it down a bit for the game to be playable. Fortunately, the module temps contient un L'horloge which is designed exactly for this purpose.

En utilisant L'horloge to establish a playable frame rate requires just two lines of code. The first creates a new L'horloge before the game loop begins:

106 # Setup the clock for a decent framerate
107 l'horloge = pygame.temps.L'horloge()

The second calls .tick() informer pygame that the program has reached the end of the frame:

188 # Flip everything to the display
189 pygame.afficher.flip()
190 
191 # Ensure program maintains a rate of 30 frames per second
192 l'horloge.tick(30)

The argument passed to .tick() establishes the desired frame rate. To do this, .tick() calculates the number of milliseconds each frame should take, based on the desired frame rate. Then, it compares that number to the number of milliseconds that have passed since the last time .tick() was called. If not enough time has passed, then .tick() delays processing to ensure that it never exceeds the specified frame rate.

Passing in a smaller frame rate will result in more time in each frame for calculations, while a larger frame rate provides smoother (and possibly faster) gameplay:

Setting the frame rate in pygame

Play around with this number to see what feels best for you!

Sound Effects

So far, you’ve focused on gameplay and the visual aspects of your game. Now let’s explore giving your game some auditory flavor as well. pygame fournit mixer to handle all sound-related activities. You’ll use this module’s classes and methods to provide background music and sound effects for various actions.

The name mixer refers to the fact that the module mixes various sounds into a cohesive whole. Using the la musique sub-module, you can stream individual sound files in a variety of formats, such as MP3, Ogg, and Mod. You can also use Du son to hold a single sound effect to be played, in either Ogg or uncompressed WAV formats. All playback happens in the background, so when you play a Du son, the method returns immediately as the sound plays.

As with most things pygame, using mixer starts with an initialization step. Luckily, this is already handled by pygame.init(). You only need to call pygame.mixer.init() if you want to change the defaults:

106 # Setup for sounds. Defaults are good.
107 pygame.mixer.init()
108 
109 # Initialize pygame
110 pygame.init()
111 
112 # Set up the clock for a decent framerate
113 l'horloge = pygame.temps.L'horloge()

pygame.mixer.init() accepts a number of arguments, but the defaults work fine in most cases. Note that if you want to change the defaults, you need to call pygame.mixer.init() before calling pygame.init(). Otherwise, the defaults will be in effect regardless of your changes.

After the system is initialized, you can get your sounds and background music setup:

135 # Load and play background music
136 # Sound source: http://ccmixter.org/files/Apoxode/59262
137 # License: https://creativecommons.org/licenses/by/3.0/
138 pygame.mixer.la musique.charge("Apoxode_-_Electric_1.mp3")
139 pygame.mixer.la musique.jouer(boucles=-1)
140 
141 # Load all sound files
142 # Sound sources: Jon Fincher
143 move_up_sound = pygame.mixer.Du son("Rising_putter.ogg")
144 move_down_sound = pygame.mixer.Du son("Falling_putter.ogg")
145 collision_sound = pygame.mixer.Du son("Collision.ogg")

Lines 138 and 139 load a background sound clip and begin playing it. You can tell the sound clip to loop and never end by setting the named parameter loops=-1.

Lines 143 to 145 load three sounds you’ll use for various sound effects. The first two are rising and falling sounds, which are played when the player moves up or down. The last is the sound used whenever there is a collision. You can add other sounds as well, such as a sound for whenever an Ennemi is created, or a final sound for when the game ends.

So, how do you use the sound effects? You want to play each sound when a certain event occurs. For example, when the ship moves up, you want to play move_up_sound. Therefore, you add a call to .play() whenever you handle that event. In the design, that means adding the following calls to .update() pour Player:

26 # Define the Player object by extending pygame.sprite.Sprite
27 # Instead of a surface, use an image for a better-looking sprite
28 classe Player(pygame.lutin.Sprite):
29     def __init__(soi):
30         super(Player, soi).__init__()
31         soi.le surf = pygame.image.charge("jet.png").convertir()
32         soi.le surf.set_colorkey((255, 255, 255), RLEACCEL)
33         soi.rect = soi.le surf.get_rect()
34 
35     # Move the sprite based on keypresses
36     def mise à jour(soi, pressed_keys):
37         si pressed_keys[[[[K_UP]:
38             soi.rect.move_ip(0, -5)
39             move_up_sound.jouer()
40         si pressed_keys[[[[K_DOWN]:
41             soi.rect.move_ip(0, 5)
42             move_down_sound.jouer()

For a collision between the player and an enemy, you play the sound for when collisions are detected:

201 # Check if any enemies have collided with the player
202 si pygame.lutin.spritecollideany(joueur, ennemis):
203     # If so, then remove the player
204     joueur.tuer()
205 
206     # Stop any moving sounds and play the collision sound
207     move_up_sound.Arrêtez()
208     move_down_sound.Arrêtez()
209     collision_sound.jouer()
210 
211     # Stop the loop
212     fonctionnement = False

Here, you stop any other sound effects first, because in a collision the player is no longer moving. Then you play the collision sound and continue execution from there.

Finally, when the game is over, all sounds should stop. This is true whether the game ends due to a collision or the user exits manually. To do this, add the following lines at the end of the program after the loop:

220 # All done! Stop and quit the mixer.
221 pygame.mixer.la musique.Arrêtez()
222 pygame.mixer.quitter()

Technically, these last few lines are not required, as the program ends right after this. However, if you decide later on to add an intro screen or an exit screen to your game, then there may be more code running after the game ends.

That’s it! Test it again, and you should see something like this:

Pygame window

A Note on Sources

You may have noticed the comment on lines 136-137 when the background music was loaded, listing the source of the music and a link to the Creative Commons license. This was done because the creator of that sound required it. The license requirements stated that in order to use the sound, both proper attribution and a link to the license must be provided.

Here are some sources for music, sound, and art that you can search for useful content:

As you make your games and use downloaded content such as art, music, or code from other sources, please be sure that you are complying with the licensing terms of those sources.

Conclusion

Throughout this tutorial, you’ve learned how game programming with pygame differs from standard procedural programming. You’ve also learned how to:

  • Implement event loops
  • Draw items on the screen
  • Play sound effects and music
  • Handle user input

To do this, you used a subset of the pygame modules, including the afficher, mixer et la musique, temps, image, un événement, et clé modules. You also used several pygame classes, including Rect, Surface, Du son, et Sprite. But these only scratch the surface of what pygame can do! Check out the official pygame documentation for a full list of available modules and classes.

You can find all of the code, graphics, and sound files for this article by clicking the link below:

Feel free to leave comments below as well. Happy Pythoning!