oujood.com

PyGame : Initiation à la programmation de jeux en Python partie 1

Dans ce tutoriel nous allons voir: Principes fondamentaux du jeu, but du jeu et aspects non couverts dans ce jeu, dessiner le joueur et ennemies, déplacement du joueur et des ennemies.

Partie 1 : Application pour apprendre à créer des jeux avec Python

Ce tutoriel vous apprendra à créer des jeux en utilisant Python et la bibliothèque PyGame. À la fin de ce cours, vous serez capable de :

  • Afficher des objets sur l'écran
  • Jouer des sons et de la musique
  • Gérer les interactions de l'utilisateur
  • Créer des boucles d'événements
  • Comprendre les différences entre la programmation de jeux et la programmation procédurale standard de Python Prérequis

Ce tutoriel suppose que vous avez une connaissance de base de la programmation Python, y compris :

  • Les fonctions définies par l'utilisateur
  • Les importations
  • Les boucles
  • Les conditions
  • L'ouverture de fichiers

Concepts de PyGame

PyGame est une bibliothèque Python qui permet de créer des jeux vidéo. Elle est portable sur différentes plates-formes et appareils, ce qui signifie qu'un jeu créé avec PyGame peut être exécuté sur un PC, un Mac ou un appareil mobile.

Pour atteindre cette portabilité, PyGame utilise des abstractions pour différentes réalités matérielles. Ces abstractions permettent de cacher les détails spécifiques de chaque plate-forme ou appareil, afin que le développeur puisse se concentrer sur la création du jeu.

Initialisation et modules

La bibliothèque PyGame est composée de plusieurs modules, chacun fournissant un accès abstrait à un aspect du matériel. Par exemple, le module display fournit un accès à l'écran, le module joystick fournit un accès aux manettes de jeu, et le module music fournit un accès aux fichiers audio.

Lorsque vous commencez un programme PyGame, vous devez d'abord importer et initialiser la bibliothèque. Cela se fait en appelant la fonction pygame.init(). Cette fonction appelle les fonctions init() de tous les modules PyGame inclus.

Images et rectangles

Votre programme pygame de base permet de dessiner une forme directement sur la surface de l'écran, mais vous pouvez également travailler avec des images sur le disque. Le module image vous permet de charger et d'enregistrer des images dans une variété de formats populaires. Les images sont chargées dans des objets Surface, qui peuvent ensuite être manipulés et affichés de nombreuses façons.

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

Voilà, assez de théorie. Concevons et écrivons un jeu !

Principes fondamentaux du jeu

Avant d'entamer le codage, il est toujours judicieux d'établir un plan de conception. Comme il s'agit d'un jeu didactique, établissons les bases du jeu :

  • L'objectif du jeu est d'esquiver les obstacles en approche :
    • Le joueur démarre du côté gauche de l'écran.
    • Les obstacles surgissent aléatoirement à droite et se déplacent horizontalement vers la gauche.
    • Le joueur peut se déplacer dans toutes les directions - gauche, droite, haut, bas - pour éviter les obstacles.
    • Il lui est impossible de sortir de l'écran.
    • Le jeu prend fin lorsque le joueur entre en collision avec un obstacle ou lorsque l'utilisateur ferme la fenêtre.

Un de mes professeurs avait l'habitude de dire lorsqu'il parlait de la conception de logiciels : "On ne sait ce qu'on doit faire que lorsque l'on prend conscience de ce qu'on ne doit pas faire." Gardant cela à l'esprit, voici quelques aspects qui ne seront pas couverts dans ce tutoriel :

  • Aucune gestion de vies multiples
  • Pas de comptabilisation de points
  • Aucune capacité d'attaque pour le joueur
  • Pas de progression à travers différents niveaux

Vous êtes libre d'expérimenter l'ajout de ces fonctionnalités ou d'autres à votre propre programme.

En avant !

Importation et initialisation de PyGame

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

Exemple :       Copier le code

# Importer le module pygame
import pygame

# Importer pygame.locals pour faciliter l'accès aux coordonnées clés
# Mise à jour pour se conformer aux standards flake8 et black
from pygame.locals import (
    K_UP,
    K_DOWN,
    K_LEFT,
    K_RIGHT,
    K_ESCAPE,
    KEYDOWN,
    QUIT,
)

# Initialiser pygame
pygame.init()

La bibliothèque pygame définit beaucoup de choses en plus des modules et des classes. Elle définit aussi des constantes locales pour des choses comme les frappes de touches, les mouvements de souris et les attributs d'affichage. Vous faites référence à ces constantes en utilisant la syntaxe pygame.<CONSTANT>. En important des constantes spécifiques depuis pygame.locals, vous pouvez utiliser la syntaxe <CONSTANT> à la place. Cela vous permettra d'économiser quelques frappes et d'améliorer la lisibilité générale.

Mise en place de l'écran

Il nous faut maintenant quelque chose pour dessiner ! Créez un écran qui servira de support (canevas) à l'ensemble du jeu :

Exemple :       Copier le code

# Importer le module pygame
import pygame

# Importer pygame.locals pour un accès plus facile aux coordonnées clés
# Mise à jour pour se conformer aux normes flake8 et black
from pygame.locals import (
    K_UP,
    K_DOWN,
    K_LEFT,
    K_RIGHT,
    K_ESCAPE,
    KEYDOWN,
    QUIT,
)

# Initialiser pygame
pygame.init()

# Définir des constantes pour la largeur et la hauteur de l'écran
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# Créer l'objet écran
# La taille est déterminée par les constantes SCREEN_WIDTH et SCREEN_HEIGHT
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

Nous créons l'écran à utiliser en appelant pygame.display.set_mode() et en lui passant un tuple ou une liste avec la largeur et la hauteur désirées. Dans ce cas, la fenêtre est de 800x600, comme défini par les constantes SCREEN_WIDTH et SCREEN_HEIGHT. Cette fonction renvoie une surface qui représente les dimensions intérieures de la fenêtre. Il s'agit de la partie de la fenêtre que nous pouvons contrôler, tandis que le système d'exploitation contrôle les bords de la fenêtre et la barre de titre.

En exécutant ce programme maintenant, il est possible de voir une fenêtre s'afficher brièvement, puis disparaître immédiatement lorsque le programme se termine. Dans la section suivante, nous nous concentrerons sur la boucle principale du jeu afin de nous assurer que notre programme ne se termine que lorsqu'il reçoit les informations correctes.

Configuration de la boucle de jeu

Tous les jeux utilisent une boucle de jeu pour contrôler le déroulement de la partie. La boucle de jeu fait quatre choses très importantes :

  • Traiter les entrées de l'utilisateur
  • Mise à jour de l'état de tous les objets du jeu
  • Mise à jour de l'affichage et de la sortie audio
  • Maintenir la vitesse du jeu
Chaque cycle de la boucle de jeu s'appelle une trame, et plus nous pouvons faire les choses rapidement à chaque cycle, plus le jeu tournera vite. Les trames se succèdent jusqu'à ce qu'une condition de sortie du jeu soit remplie. Dans notre conception, deux conditions peuvent mettre fin à la boucle du jeu :
  1. Le joueur entre en collision avec un obstacle. (nous reviendrons sur la détection des collisions plus tard).
  2. Le joueur ferme la fenêtre.

La première chose que fait la boucle du jeu est de traiter les entrées de l'utilisateur pour permettre au joueur de se déplacer sur l'écran. Par conséquent, nous avons besoin d'un moyen de capturer et de traiter une variété d'entrées. ce que nous pouvons faire en appliquant le système d'événements de pygame.

Traitement des événements

Les pressions sur les touches, les mouvements de la souris et même les mouvements du joystick(manette de jeux) sont quelques-uns des moyens par lesquels un utilisateur peut fournir des données. Toutes les entrées de l'utilisateur entraînent la création d'un événement. Les événements peuvent se produire à tout moment et proviennent souvent (mais pas toujours) de l'extérieur du programme. Tous les événements de pygame sont placés dans la file d'attente des événements, à laquelle on peut accéder et que l'on peut manipuler. La gestion des événements est appelée "handling", et le code permettant de le faire est appelé "event handler" (gestionnaire d'événements).

Chaque événement dans pygame est associé à un type d'événement. Pour notre jeu, les types d'événements sur lesquels nous allons nous concentrer sont l'appui sur une touche et la fermeture d'une fenêtre. Les événements de pression de touche ont le type d'événement KEYDOWN, et l'événement de fermeture de fenêtre a le type QUIT. D'autres données peuvent être associées aux différents types d'événements. Par exemple, le type d'événement KEYDOWN possède également une variable appelée key pour indiquer quelle touche a été pressée.

Nous accédons à la liste de tous les événements actifs dans la file d'attente en appelant pygame.event.get(). Nous parcourons ensuite cette liste en boucle, nous vérifions chaque type d'événement et nous réagissons en conséquence :

Exemple :       Copier le code

	# Variable permettant de maintenir la boucle principale en cours d'exécution
	running = True

	# Boucle principale
	while running :
		# Regarde chaque événement dans la file d'attente
		for event in pygame.event.get() :
			# L'utilisateur a-t-il appuyé sur une touche ?
			if event.type == KEYDOWN :
				# Était-ce la touche Escape ? Si c'est le cas, arrêter la boucle.
				if event.key == K_ESCAPE :
					running = False

			# L'utilisateur a-t-il cliqué sur le bouton de fermeture de la fenêtre ? Si c'est le cas, arrêter la boucle.
			elif event.type == QUIT :
				running = False
	pygame.quit()
        

Examinons de plus près cette boucle de jeu :

  • 1. La variable running est une variable booléenne qui permet de maintenir la boucle principale en cours d'exécution. Tant que running est True, la boucle continuera à s'exécuter.
  • 2. La boucle principale commence par une boucle while qui s'exécute tant que la variable running est True.
  • 3. À l'intérieur de la boucle, la fonction pygame.event.get() est utilisée pour récupérer tous les événements de la file d'attente d'événements. La variable event est une variable de type pygame.event.Event qui contient des informations sur l'événement.
  • 4. La première condition vérifie si l'utilisateur a appuyé sur une touche. Si c'est le cas, la boucle passe à l'instruction suivante.
  • 5. La deuxième condition vérifie si la touche appuyée est la touche Escape. Si c'est le cas, la variable running est mise à False pour arrêter la boucle principale.
  • 6. La troisième condition vérifie si l'utilisateur a cliqué sur le bouton de fermeture de la fenêtre. Si c'est le cas, la variable running est mise à False pour arrêter la boucle principale.

En dehors de la boucle principale, il est important de fermer la fenêtre Pygame en appelant la fonction pygame.quit(). Cela libérera les ressources utilisées par Pygame.

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

pygame window

La fenêtre disparaîtra seulement lorsque vous appuierez sur la touche Esc ou lorsque vous déclencherez un événement QUIT en fermant la fenêtre.

Dessiner à l'écran

Nous pouvons Dessiner à l'écran à l'aide de trois façons:

  • 1. La commande fill() exemple : screen.fill() pour remplir l'arrière-plan
  • 2. La commande pygame.draw pour dessiner une forme exemple: pygame.draw.circle() pour dessiner un cercle
  • 3. La troisième façon de dessiner à l'écran : en utilisant une Surface.

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

# Remplir l'écran de blanc
screen.fill((255, 255, 255))

# Créer une surface et passer un tuple contenant sa longueur et sa largeur
surf = pygame.Surface((50, 50))

# Donner une couleur à la surface pour la séparer de l'arrière-plan
surf.fill((0, 0, 0))
rect = surf.get_rect()

Ce code remplit d'abord l'écran de blanc en utilisant la fonction pygame.display.fill(). La fonction prend en entrée un tuple de trois valeurs représentant les composantes rouge, verte et bleue de la couleur. Dans ce cas, les trois composantes sont égales à 255, ce qui correspond à la couleur blanche.

Ensuite, le code crée une surface de 50 pixels de large et 50 pixels de haut en utilisant la fonction pygame.Surface(). La fonction prend en entrée un tuple contenant la longueur et la largeur de la surface.

Enfin, le code donne une couleur noire à la surface en utilisant la fonction pygame.Surface.fill(). La fonction prend en entrée un tuple de trois valeurs représentant les composantes rouge, verte et bleue de la couleur. Dans ce cas, les trois composantes

sont égales à 0, ce qui correspond à la couleur noire.

La variable rect contient les coordonnées et la taille de la surface. Elle peut être utilisée pour placer la surface sur l'écran.

Utilisation de .blit() et .flip()

Il ne suffit pas de créer une nouvelle surface pour la voir à l'écran. Pour ce faire, il est nécessaire de fusionner la surface avec une autre surface. Le terme blit signifie Block Transfer (transfert de bloc) et .blit() permet de copier le contenu d'une surface sur une autre. Vous ne pouvez utiliser la fonction .blit() que d'une surface à l'autre, mais comme l'écran n'est qu'une autre surface, ce n'est pas un problème. Voici comment dessiner un surf sur l'écran :

# Cette ligne dit "Dessine un objet surf sur l'écran au centre"
screen.blit(surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
pygame.display.flip()

L'appel .blit() prend deux arguments :

  • 1. La surface à dessiner (surf)
  • 2. L'emplacement où la dessiner sur la surface source

    Les coordonnées (SCREEN_WIDTH/2, SCREEN_HEIGHT/2) indiquent à votre programme de placer le surf au centre exact de l'écran, mais ce n'est pas tout à fait le cas :

La raison pour laquelle l'image semble décentrée est que .blit() place le coin supérieur gauche de surf à l'endroit indiqué. Si vous souhaitez que l'image soit centrée, vous devez effectuer des calculs pour la décaler vers le haut et vers la gauche. Vous pouvez le faire en soustrayant la largeur et la hauteur de surf de la largeur et de la hauteur de l'écran, en divisant chacune de ces valeurs par 2 pour localiser le centre, puis en passant ces nombres comme arguments à screen.blit() :

Exemple :       Copier le code

# Placer le centre du surf au centre de l'écran
surf_center = (
    (SCREEN_WIDTH-surf.get_width())/2,
    (SCREEN_HEIGHT-surf.get_height())/2
)

# Dessine le surf aux nouvelles coordonnées
screen.blit(surf, surf_center)
pygame.display.flip()

Notez l'appel à pygame.display.flip() après l'appel à blit(). Cela met à jour l'écran entier avec tout ce qui a été dessiné depuis le dernier retournement. Sans l'appel à .flip(), rien n'est affiché.

Les Sprites

Dans la conception de notre jeu, le joueur commence à gauche et les obstacles arrivent par la droite. Nous pouvons représenter tous les obstacles par des objets Surface 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 ? Que faire si vous voulez dessiner des images d'arrière-plan qui bougent également ? Et si vous voulez que vos images soient animées ? Les sprites permettent de gérer toutes ces situations et bien d'autres encore.

En termes de programmation, un sprite est une représentation en 2D de quelque chose à l'écran. Pygame fournit une classe Sprite, qui est conçue pour contenir une ou plusieurs représentations graphiques de n'importe quel objet de jeu que vous souhaitez afficher à l'écran. Pour l'utiliser, nous devons créer une nouvelle classe qui étend la classe Sprite. Cela nous permet d'utiliser ses méthodes intégrées.

Les Joueurs

Voici comment utiliser les objets Sprite dans le jeu en cours pour définir le joueur.

Exemple :       Copier le code

# Définir un objet Player en étendant pygame.sprite.Sprite
# La surface dessinée sur l'écran est maintenant un attribut de 'player'
class Player(pygame.sprite.Sprite) :
    def __init__(self) :
        super(Player, self).__init__()
        self.surf = pygame.Surface((75, 25))
        self.surf.fill((255, 255, 255))
        self.rect = self.surf.get_rect()

Nous définissons d'abord Player en étendant pygame.sprite.Sprite. Ensuite, .__init__() utilise .super() pour appeler la méthode .__init__() de Sprite. Pour plus d'informations sur la nécessité de cette méthode, vous pouvez lire Supercharge Your Classes With Python super().

Ensuite, nous définissons et initialisons .surf pour contenir l'image à afficher, qui est actuellement une boîte blanche. Nous définissons et initialisons également .rect, que nous utiliserons plus tard pour dessiner le lecteur. Pour utiliser cette nouvelle classe, nous devons créer un nouvel objet et modifier le code de dessin.

Voici l'ensemble du code: :

Exemple :       Copier le code

# Importer le module pygame
import pygame

# Importer pygame.locals pour faciliter l'accès aux coordonnées clés
# Mise à jour pour se conformer aux normes flake8 et black
from pygame.locals import (
    K_UP,
    K_DOWN,
    K_LEFT,
    K_RIGHT,
    K_ESCAPE,
    KEYDOWN,
    QUIT,
)

# Définir des constantes pour la largeur et la hauteur de l'écran
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 300

# Définir un objet joueur en étendant pygame.sprite.Sprite
# La surface dessinée sur l'écran est maintenant un attribut de 'player'
class Player(pygame.sprite.Sprite) :
    def __init__(self) :
        super(Player, self).__init__()
        self.surf = pygame.Surface((75, 25))
        self.surf.fill((255, 255, 255))
        self.rect = self.surf.get_rect()

# Initialiser pygame
pygame.init()

# Créer l'objet écran
# La taille est déterminée par les constantes SCREEN_WIDTH et SCREEN_HEIGHT
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

# Instanciation du joueur. Pour l'instant, c'est juste un rectangle.
player = Player()

# Variable pour maintenir la boucle principale en cours d'exécution
running = True

# Boucle principale
while running :
    # Boucle de recherche dans la file d'attente des événements
    for event in pygame.event.get() :
        # Vérifier la présence d'un événement KEYDOWN
        if event.type == KEYDOWN :
            # Était-ce la touche Escape ? Si c'est le cas, arrêter la boucle.
            if event.key == K_ESCAPE :
                running = False
        # Vérifier s'il y a un événement QUIT. Si c'est le cas, mettre running à false.
        elif event.type == QUIT :
            running = False
    
    # Remplir l'écran de noir
    screen.fill((0, 0, 0))

    # Dessine le joueur sur l'écran
    screen.blit(player.surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))

    # Mettre à jour l'affichage
    pygame.display.flip()

pygame.quit()

Ce code donne :

image écran avec un joueur

Que pensez-vous qu'il se passerait si vous remplaciez la ligne 59 par screen.blit(player.surf, player.rect) ? Essayez pour voir.

Lorsque vous passez un Rect à .blit(), il utilise les coordonnées du coin supérieur gauche pour dessiner la surface. Nous l'utiliserons plus tard pour faire bouger le joueur !

Cliquer ici pour voir la 2èmé partie




Voir aussi nos tutoriel :

margin-bottom

Définit la marge bas d'un élément

CSS border-right

Définit toutes les propriétés de bordure à droite dans une déclaration

L'attribut style

Spécifie un style CSS inline à un élément