Pygame est une librairie très populaire en Python, utilisée afin de créer des jeux 2D relativement simplement. Pour ceux qui ont déjà fait du C, vous connaissez sûrement la SDL, en fait Pygame est simplement une adaptation de cette librairie en Python.
Allez c'est parti ! Mais avant de commencer, nous allons voir ce qu'est une interface graphique, puisque c'est sur cela que repose Pygame.
Souvent, lorsque vous apprenez un nouveau langage de programmation, vous commencez par réaliser des programmes en console. Cela permet de travailler réellement sur le code, sans se préoccuper du design, de l'audio, etc... Un tel programme est utile lorsque vous n'avez pas besoin d'utiliser un design spécifique pour votre programme.
Mais lorsque vous souhaitez réaliser un jeu, l'expérience utilisateur est très importante, il est donc nécessaire de gérer le design. C'est pour cela qu'on utilise des interfaces graphiques. Cela a pour conséquence de faire perdre des performances contrairement à un programme en console, mais ça a pour avantage de procurer un affichage intuitif, avec la possibilité d'ajouter des images et de réaliser un vrai design.
Un programme qui tourne via en interface graphique a un fonctionnement bien différent par rapport à un programme qui tourne en console.
En effet, un programme en console consiste la plupart du temps en une suite d'actions successives et bien séquencées. Par exemple : on demande à l'utilisateur d'entrer des données => on attend ces données => on demande d'autres données => on attend => etc... Ce qu'il se passe dans le programme est en fait contrôlé par le programme lui-même.
En interface graphique, on va par exemple afficher un personnage, et le programme va réagir lorsqu'un utilisateur appuiera sur une touche, ou fera un clic de souris. Ici, ce sont les actions effectuées par l'utilisateur qui contrôlent le programme. On parle d'évènements. Il faut donc faire un programme qui va écouter différents évènements et réagir à ces évènements, plutôt qu'on programme qui va créer ses propres évènements.
Comme toute librairie que vous souhaiteriez utiliser, il faut d'abord l'installer. Pour cela, on utilise la commande classique :
pip install pygame
Egalement, si vous souhaitez utiliser les mêmes ressources que moi lors de cet article, vous pouvez télécharger les ressources en cliquant ici. C'est un fichier zip que vous pouvez simplement extraire dans le projet que vous avez créé pour suivre cette article.
Pygame est divisée en différents modules répondant chacun à un rôle bien précis :
Finalement, Pygame dispose aussi de constantes contenues dans pygame.locals
.
La première chose à faire lorsque vous faites un programme en interface graphique, c'est évidemment de créer cette interface graphique. Nous allons donc commencer par là. Mais avant toute chose, il est nécessaire d'initialiser Pygame, via la fonction init()
.
import pygame
pygame.init()
On peut maintenant créer notre fenêtre. Pour créer une fenêtre avec Pygame, on utilise la fonction set_mode
du module display
. Cette fonction prend en paramètre la taille de votre fenêtre sous forme d'un tuple, et éventuellement des paramètres pour définir certaines propriétés de la fenêtre (la rendre redimensionnable, la mettre en plein écran, etc...)
import pygame.locals
window = pygame.display.set_mode((640, 360), pygame.locals.RESIZABLE)
Ici, on crée une fenêtre de largeur 640 et de hauteur 360, redimensionnable, qu'on stocke dans une variable. L'objet renvoyé par la fonction est une Surface, c'est un type particulier de Pygame, nous permettant d'afficher des images. Si vous avez exécuté le code, vous avez dû constater que votre fenêtre s'ouvre et se ferme quasiment instantanément. C'est normal, puisque vous n'avez pas d'instructions après la déclaration de votre fenêtre, donc votre programme s'arrête.
Pour bloquer le programme et donc maintenir la fenêtre, il faut utiliser une boucle infinie. Voici donc le code minimal pour ouvrir et maintenir une fenêtre :
import pygame.display
import pygame.locals
pygame.init()
window = pygame.display.set_mode((640, 360), pygame.locals.RESIZABLE)
stop = 0
while not stop:
continue
Mais de cette façon, vous ne pouvez pas fermer votre fenêtre proprement. Pour l'instant, le seul moyen de la fermer est d'arrêter votre programme (Ctrl+C).
Maintenant que nous avons notre fenêtre, nous aimerions bien la décorer un peu. On va commencer par ajouter une image de fond. Pour cela, il faut d'abord charger notre image. On utilise la fonction pygame.image.load
à laquelle on passe en paramètre le chemin vers notre image. En plus de cela, on va également convertir notre image. On utilise donc la méthode convert
de notre image. Ce qui donne ceci :
import pygame.image
bg = pygame.image.load("ressources/bg.jpg").convert()
Maintenant, il faut coller cette image sur notre fenêtre. On utilise pour cela la méthode blit
de notre fenêtre à laquelle on passe l'image que l'on souhaite coller, ainsi que les coordonnées de collage sous forme de tuple. Si vous ne savez pas quelles coordonnées mettre, voici le repère associé à notre fenêtre :
Ce dessin vous permet aussi de comprendre comment Pygame gère les coordonnées. Ainsi, l'origine du repère est située dans le coin supérieur gauche de votre fenêtre, et les valeurs positives sont situées vers la gauche et le bas pour les axes.
On souhaite que notre fond recouvre toute la fenêtre, on va donc le coller en (0, 0).
window.blit(bg, (0, 0))
Mais si vous essayez d'afficher votre fenêtre, vous ne verrez pas votre image apparaître. En effet, à chaque fois que vous effectuez une modification graphique sur la fenêtre, il faut l'actualiser afin que cette modification soit visible. Pour cela on utilise pygame.display.flip()
après avoir collé notre image. Voici donc le code comprenant l'ajout de l'image :
import pygame.display
import pygame.image
import pygame.locals
window = pygame.display.set_mode((640, 360), pygame.locals.RESIZABLE)
bg = pygame.image.load("ressources/bg.jpg").convert()
window.blit(bg, (0, 0))
pygame.display.flip()
stop = 0
while not stop:
continue
Bien ! Maintenant si vous exécutez ce code, vous voyez votre image remplir toute la fenêtre. Maintenant, nous allons rajouter un personnage qui sera contrôlé par le joueur. De la même manière, on importe notre image et on la colle.
mario = pygame.image.load("ressources/mario.png").convert()
window.blit(bg, (0, 0))
Cependant, si vous affichez votre fenêtre, vous verrez que la transparence de notre personnage n'est pas conservée. Pour conserver notre transparence, on remplace convert
par convert_alpha
.
mario = pygame.image.load("ressources/mario.png").convert_alpha()
Puis, juste pour que ce soit un peu cohérent, on va coller notre personnage en (0, 251)
histoire qu'il touche le sol, modifiez donc la position dans blit
.
Bon, on a une fenêtre, un fond, un personnage, maintenant on voudrait déplacer ce personnage. Nous allons donc découvrir les évènements.
J'ai évoqué brièvement ce que sont les évènements au début de cet article, je vais donc revenir dessus rapidement. Un évènement, c'est quelque chose que le programme peut "capter", venant de l'utilisateur. Par exemple, un clic de souris, un mouvement de souris, ou l'appui sur une touche de clavier.
Nous allons commencer avec un évènement basique afin de mieux contrôler notre fenêtre.
Pour fermer notre fenêtre, il va falloir détecter le clic d'un utilisateur sur le bouton de fermeture. Afin de gérer les évènements, Pygame utilise la fonction pygame.event.get()
qui détecte et renvoie une liste de tous les évènements se produisant à l'instant où elle est appelée. Il nous suffit donc de vérifier cette liste à tout instant, et de voir si elle contient l'évènement de clic sur le bouton de fermeture de la fenêtre. Pour cela, on va vérifier un par un chaque évènement contenu dans la liste. Et ceci à chaque fois qu'on fait un tour dans notre boucle infinie. Voici le code :
stop = 0
while not stop:
for event in pygame.event.get(): # On récupère tous les évènements
if event.type == pygame.locals.QUIT: # Si on détecte un clic sur le bouton "Quitter", on passe "stop" à 1
stop = 1
C'est quoi pygame.locals.QUIT
?
C'est une constante qui indique que l'évènement est de type QUIT
(c'est à dire que l'utilisateur a cliqué sur la petite croix en haut à droite de la fenêtre). Si on détecte cet évènement, alors on passe stop
à 1, donc la condition dans le while
n'est plus valide, et le programme s'arrête. Vous pouvez tester, vous verrez qu'en cliquant sur la croix, votre fenêtre se ferme proprement.
Les évènements de clavier sont un type d'évènement correspondant à l'appui d'une touche sur votre clavier. Ces évènements sont identifiés par le type pygame.locals.KEYDOWN
. Et pour accéder à la touche appuyée, on utilise event.key
qui nous renvoie une constante. event.key
peut prendre les valeurs suivantes, qui représentent donc tout votre clavier :
K_a
, K_b
, ..., k_z
K_0
, ..., K_9
K_TAB
, K_RETURN
, K_ESCAPE
, ...La liste complète se trouve ici : Pygame Keys Doc.
Essayez maintenant de créer un nouvel évènement : lorsque l'utilisateur appuie sur la touche E, la fenêtre se ferme. Il suffit en fait de vérifier le type de l'évènement comme toute à l'heure, et si c'est un évènement KEYDOWN
, alors on vérifie la touche appuyée avec event.key
. Si cette touche est le E, alors on ferme la fenêtre.
Maintenant, qu'on sait gérer les évènements de clavier, on va pouvoir définir des touches pour contrôler notre personnage !
On va supposer que notre personnage ne peut pas aller vers le haut, ni vers le bas, puisqu'il ne sait pas sauter et qu'il y a un sol en dessous de lui. Il ne peut donc se déplacer que vers la gauche ou vers la droite. Pour le contrôler, nous allons utiliser deux touches, la touche Q (pour aller à gauche) et la touche D (pour aller à droite). D'un point de vue procédural, il faut que lorsque l'on appuie sur Q, on diminue la position de notre image sur l'axe horizontal, et lorsque l'on appuie sur D on doit l'augmenter.
Il faut donc d'abord récupérer les coordonnées de notre personnage. Elles sont stockées dans une classe appelée Rect
. On va donc récupérer cet objet dès le chargement de notre personnage, avec la méthode get_rect
d'une image. Voici donc le chargement de notre personnage en récupérant ses coordonnées.
mario = pygame.image.load("ressources/mario.png").convert_alpha()
position_mario = mario.get_rect()
La position par défaut est (0, 0)
, on doit donc changer cette position initiale. On utilise pour cela la méthode update
de Rect
qui prend en paramètres la position x, position y, la largeur, et la hauteur. On ne veut pas changer la taille de notre image, donc on va simplement utiliser les deux premiers paramètres qu'on va mettre à (0, 251)
.
position_mario.update(0, 251, 0, 0)
Maintenant qu'on a un objet stockant la position de notre image, on peut blit
notre image en utilisant cet objet à la place d'un tuple classique pour la position.
window.blit(mario, position_mario)
Voici donc le code à ce stade :
import pygame.display
import pygame.image
import pygame.locals
window = pygame.display.set_mode((640, 360), pygame.locals.RESIZABLE)
bg = pygame.image.load("ressources/bg.jpg").convert()
mario = pygame.image.load("ressources/mario.png").convert_alpha()
position_mario = mario.get_rect()
position_mario.update(0, 251, 0, 0)
window.blit(bg, (0, 0))
window.blit(mario, position_mario)
pygame.display.flip()
stop = 0
while not stop:
for event in pygame.event.get():
if event.type == pygame.locals.QUIT:
stop = 1
if event.type == pygame.locals.KEYDOWN:
if event.key == pygame.locals.K_q:
stop = 1
Si nous nous sommes embêtés à modifier notre code pour intégrer notre objet position_mario
, c'est pour une simple raison : cet objet dispose d'une méthode move
prenant en paramètres deux entiers représentant respectivement un déplacement selon x, et un déplacement selon y., et renvoyant la position de notre image déplacée. On a maintenant tous les outils pour déplacer notre personnage, allons-y !
On commence par intégrer les évènements :
if event.key == pygame.locals.K_q:
position_mario = position_mario.move(-3, 0)
if event.key == pygame.locals.K_d:
position_mario = position_mario.move(3, 0)
On actualise simplement la position lorsqu'on détecte un appui sur les touches définies plus haut.
Maintenant, il faut à nouveau coller notre image. Mais attendez, car si vous collez votre personnage directement, vous verrez toujours en plus l'ancien personnage. Testez et vous verrez.
Il faut donc nettoyer la fenêtre avant de bouger notre personnage. Pour cela, on recolle simplement notre fond de base.
window.blit(bg, (0, 0))
Ensuite, on peut coller notre personnage :
window.blit(mario, position_mario)
Finalement, il faut actualiser la fenêtre :
pygame.display.flip()
On obtient le code final suivant :
import pygame.display
import pygame.image
import pygame.locals
window = pygame.display.set_mode((640, 360), pygame.locals.RESIZABLE)
bg = pygame.image.load("ressources/bg.jpg").convert()
mario = pygame.image.load("ressources/mario.png").convert_alpha()
position_mario = mario.get_rect()
position_mario.update(0, 251, 0, 0)
window.blit(bg, (0, 0))
window.blit(mario, position_mario)
pygame.display.flip()
stop = 0
while not stop:
for event in pygame.event.get():
if event.type == pygame.locals.QUIT:
stop = 1
if event.type == pygame.locals.KEYDOWN:
if event.key == pygame.locals.K_e:
stop = 1
if event.key == pygame.locals.K_q:
position_mario = position_mario.move(-3, 0)
if event.key == pygame.locals.K_d:
position_mario = position_mario.move(3, 0)
window.blit(bg, (0, 0))
window.blit(mario, position_mario)
pygame.display.flip()
Vous pouvez le tester, et vous verrez qu'on peut déplacer notre personnage. On peut même le déplacer en dehors de la fenêtre.
Par contre, il faut bourriner sur notre clavier pour le déplacer. On préfèrerait laisser appuyez sur la touche D par exemple pour que notre personnage avance plutôt que d'appuyer 50 fois. Pour cela, on utilise pygame.key.set_repeat
. Cette fonction prend en premier paramètre la temps avant de commencer la répétition de touches, et en second paramètre le délai entre chaque répétition. Placez la n'importe où, par exemple avant la boucle while
:
pygame.key.set_repeat(1, 30)
Testez avec d'autres valeurs pour les paramètres si vous ne comprenez pas bien ce que ça change.
Pour finir, on va voir les évènements à la souris. Si vous faites un clic de souris, le type d'évènement est MOUSEBUTTONDOW
(ou MOUSEBUTTONUP
lorsqu'on relâche le bouton). Un tel évènement possède deux attributs : le bouton cliqué (récupérable via event.button
et la position du clic (event.pos
). Voici les valeurs possibles pour event.button
:
On va par exemple créer un évènement pour faire sauter notre personnage lorsque l'on fait un clic gauche.
Voici l'évènement à rajouter :
if event.type == pygame.locals.MOUSEBUTTONDOWN:
if event.button == 1:
for i in range(20):
side = -1
if i > 9:
side = 1
position_mario = position_mario.move(0, 3*side)
window.blit(bg, (0, 0))
window.blit(mario, position_mario)
pygame.display.flip()
pygame.time.delay(30)
Notez que j'ai utilisé pygame.time.delay
qui permet de mettre en pause le programme pour x
millisecondes, x
étant donné en paramètre. Cela rend le saut mieux visible. Normalement le reste du code est compréhensible pour vous.
Voici donc tout le code de notre petit jeu :
import pygame.display
import pygame.image
import pygame.locals
import pygame.time
window = pygame.display.set_mode((640, 360), pygame.locals.RESIZABLE)
bg = pygame.image.load("ressources/bg.jpg").convert()
mario = pygame.image.load("ressources/mario.png").convert_alpha()
position_mario = mario.get_rect()
position_mario.update(0, 251, 0, 0)
window.blit(bg, (0, 0))
window.blit(mario, position_mario)
pygame.display.flip()
pygame.key.set_repeat(1, 30)
stop = 0
while not stop:
for event in pygame.event.get():
if event.type == pygame.locals.QUIT:
stop = 1
if event.type == pygame.locals.KEYDOWN:
if event.key == pygame.locals.K_e:
stop = 1
if event.key == pygame.locals.K_q:
position_mario = position_mario.move(-3, 0)
if event.key == pygame.locals.K_d:
position_mario = position_mario.move(3, 0)
if event.type == pygame.locals.MOUSEBUTTONDOWN:
if event.button == 1:
for i in range(20):
side = -1
if i > 9:
side = 1
position_mario = position_mario.move(0, 3*side)
window.blit(bg, (0, 0))
window.blit(mario, position_mario)
pygame.display.flip()
pygame.time.delay(30)
window.blit(bg, (0, 0))
window.blit(mario, position_mario)
pygame.display.flip()
Avant de finir, il reste un type d'évènement de souris dont nous n'avons pas parlé : le déplacement de souris.
Son type est MOUSEMOTION
. Il possède 3 attributs :
Voilà, c'est tout pour les évènements de souris, et même pour les autres évènements d'ailleurs puisque c'est déjà la fin de l'article !
Voilà, vous avez pu créer un petit jeu (bon, pas ouf, mais c'est un début) ! Nous verrons prochainement comment créer des jeux plus élaborés lors d'un petit projet, même si en soi vous avez déjà pas mal d'éléments pour réaliser des jeux.
Si vous avez des questions, ou des retours à me faire par rapport à l'article, n'hésitez pas à laisser un commentaire, ou à me contacter par mail ou via l'onglet disponible sur le site !
On se retrouve prochainement afin de voir tout ça en application dans un petit projet !