C7 - Techniques et animation
Pygame n'étant plus un secret pour nous, il est alors temps de donner quelques astuces qui pourraient se révéler utiles afin d'embeillir nos jeux.
Note
Cette page n'est pas encore finie. Des améliorations doivent encore être apportées.
1 - Sauvegarder le meilleur score
Python offre une fonction open
qui nous permet de travailler avec des fichiers (généralement des textes). Ces fichiers sont utiles pour mémoriser une information même après la fermeture du jeu. Les fichiers permettent de sauvegarder des données. Un exemple commun est le meilleur score du joueur. Pour ouvrir un fichier nommé texte.txt
localisé dans le même répertoire que le projet, on procèderait ainsi :
desc_fichier = open("texte.txt")
contenu_fichier = desc_fichier.read()
liste_lignes = desc_fichier.readlines()
desc_fichier.close()
Info
Comme nous pouvons le remarquer, nous pouvons lire le contenu d'un fichier de deux manières différentes. Soit en intégralité grâce la méthode read
, le résultat obtenu est alors une chaîne de caractères représentant le contenu du fichier. Soit ligne par ligne grâce à la méthode readlines
, le résultat obtenu est alors une liste de chaînes de caractères où chaque chaîne représente une ligne du fichier initial.
L'instruction close
est indispensable mais nous pouvons l'omettre si nous utilisons le mot-clé with
. Pour cela nous devons respecter la syntaxe suivante :
with open(chemin_fichier) as desc_fichier:
contenu_fichier = desc_fichier.read()
print(contenu_fichier)
Warning
Faisons très attention aux sauts de ligne lorsqu'on manipule des fichiers ! Les sauts de ligne sont représentés par un caractère particulier : \n
Mais comment est-ce qu'on sauvegarde le meilleur score alors ? En supposant que nous sauvegardons le score du joueur dans une variable score
, il nous suffit de lire le meilleur score actuel contenu dans un fichier highscore.txt
et de le comparer avec score
. Si score
est supérieur au meilleur score, on efface le contenu de highscore.txt
et on y écrit à la place la valeur de score
qui devient alors le nouveau meilleur score. Pour cela, en plus de devoir lire le contenu d'un fichier, nous devrons écrire dans un fichier. C'est le rôle de la méthode write
. Le fichier highscore.txt
devrait ressembler à cela.
def lire_score(fichier):
with open(fichier) as fichier_score:
return int(fichier_score.read()) # Le score doit être un nombre entier
if score > lire_score("highscore.txt"):
with open("highscore.txt", "w") as fic_score:
fic_score.write(str(score)) # On ne peut écrire que des chaînes de caractères
Le programme çi-dessus est un exemple de code qui sauvegarde le meilleur score. Distinguons le "w"
dans la fonction open
. Ce paramètre indique que l'on ouvre notre fichier en mode écriture. Quand on ouvre un fichier d'une telle manière, il devient à nouveau vierge, l'intégralité son contenu initial est supprimé. Faites donc très attention lorsque vous ouvrirez un fichier en mode écriture ! Ouvrir un fichier de cette manière nous permet d'utiliser la méthode write
. Cette méthode permet d'écrire une chaîne de caractères dans le fichier associé.
2 - Animation
C'est tout de même dommage qu'un jeu puisse ressembler à ça :
C'est sans vie tout ça ! Ce pourquoi nous devons animer ce personnage. Les sprites auront ainsi l'air plus vivant. Tout d'abord c'est quoi l'animation ? L'animation, c'est une série d'images qui s'enchainent les unes après les autres. Avec souvent peu de différences entre deux images consécutives, ce qui donne l'impression que l'image est "vivante". Avec le langage Python, cela est plutôt simple à réaliser. Prenons cette image qui complète l'autre et tentons de donner vie à notre sprite !
La stratégie à employer est d'utiliser une liste d'images. Donc nous devons commencer par écrire une fonction qui prend en paramètre un répertoire et renvoie une liste de surfaces obtenues grâce à chaque fichier .png présent dans le répertoire :
import os
def images_repertoire(repertoire):
liste_images = []
for nom_fichier in os.listdir(repertoire): # Parcourt le contenu du répertoire
if nom_fichier[-4:] == ".png": # Teste si le fichier sélectionné est un fichier .png
image = pygame.image.load(repertoire + "/" + nom_fichier).convert_alpha()
liste_images.append(image)
return liste_images
A partir de cette liste d'images, nous devons effectuer un parcours circulaire. C'est-à-dire que lorsque nous atteignons la fin de la liste, nous reprenons l'image de départ. Ce qui rend l'animation infinie. Nous n'allons pas utiliser de boucles car l'enchainement d'images serait trop rapide. Nous allons plutôt définir un attribut indice_animation
représentant l'indice de l'image actuellement sélectionnée dans la liste. Il ne suffit alors plus qu'à incrémenter indice_animation
d'un petit nombre à virgule comme \(0.1\). Voyons ce que tout cela donne en Python. Commencons par la construction de notre sprite :
class AnimSprite(pygame.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.images_animation = images_repertoire("docs/images/personnage")
self.indice_animation = 0
self.vitesse_animation = 0.12
self.image = self.images_animation[self.indice_animation]
self.rect = self.image.get_rect(center=pos)
def animer(self):
self.indice_animation += self.vitesse_animation
self.image = self.images_animation[int(self.indice_animation % len(self.images_animation))]
def update(self):
self.rect.x += 6
self.animer()
Nous ajoutons trois attributs à notre sprite. Le premier, images_animation
correspond à la liste d'images qui est parcourue. Le deuxième, indice_animation
correspond à l'indice de l'image actuellement sélectionnée dans la liste images_animation
. Enfin, le troisième et dernier nouvel attribut correspond à la vitesse à laquelle les images s'enchainent. Une méthode animer
est également définie. Celle-ci incrémente indice_animation
de sorte que les images s'enchainent et met à jour l'attribut image
selon la valeur de l'indice. De plus, l'instruction :
self.indice_animation % len(self.images_animation)
Est importante car c'est ce qui permet à notre parcours d'être circulaire. On s'assure également à ce que l'indice soit un entier d'où la conversion en int
.
Dans le code principal, nous n'avons plus qu'à ajouter :
player = AnimSprite((-200, SCREEN_HEIGHT / 2))
group_player = pygame.sprite.GroupSingle(player)
while running:
# Boucle évènementielle, arrière-plan etc
group_player.draw(screen)
group_player.update()
# Tick, display.update etc
Ce qui nous permet enfin d'obtenir le résultat suivant :
L'animation peut devenir plus complexe. En effet un sprite peut prendre une image adaptée à chaque situation. Par exemple, il peut y avoir une animation de saut, de tir ou encore d'explosion. A vous de décider quelle image utiliser dans chaque contexte qui se présente.
3 - Game over et menu start
Le Game Over est un élément propre au jeu vidéo ! Pour implément cela en Python, il faut considérer le Game Over comme un état. Nous devons considérer que le jeu posséde un état actif, c'est-à-dire que le joueur joue actuellement au jeu. Et au contraire, un état inactif où un écran Game Over est présenté au joueur. En python, il suffit donc d'utiliser des instructions if
et else
pour programmer ces deux états :
actif = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and not actif:
actif = True
# Réinitialisation du jeu
if actif:
# Instructions du jeu
if condition_game_over:
actif = False
else:
# Instructions du game over
Vous devrez remplacer condition_game_over
par la condition selon laquelle le joueur perd la partie (une collision avec un ennemi par exemple).
4 - Images sur le web
Trouver des images
Où trouver des images sans avoir de problèmes de copyright ? Voici quelques sites sur lesquels vous pourrez retrouver les ressources nécessaires au développement de vos jeux :
- Un site à but non lucratif qui propose plein d'images, de fichiers audios et même des polices d'écriture en rapport avec le jeu vidéo : opengameart.org
- Un site sur lequel trouver des icones et logo :