Dans le module coin.py, chaque fois que l'on crée un objet Coin, on doit accéder au système de fichier pour charger le fichier image. S'il y a 200 Coin dans le niveau, cela peut devenir long. D'autant qu'il faudra faire de même pour les plates-formes et tout le reste.
Il est plus efficace de ne charger qu'une fois. On met donc en place un cache, c'est à dire un système qui garde les chargements en mémoire. À la première demande de dollar.png, on fait le chargement. Mais on garde le résultat de côté de sorte qu'à la prochaine demande, il ne sera plus utile de charger.
À titre d'exemple, je vais ajouter les plates-formes
On crée un dossier images qui contiendra les images de l'archive : images.zip. Ainsi toutes les images seront bien rangées.
On crée ensuite le gestionnaire :
#image.py
import pygame
IMAGES = {
"dollar":{
"filename": 'dollar.png',
"size":(20, 20)
},
"upleft":{
"filename": 'upleft.png',
"size": (30,30)
},
"upright":{
"filename": 'upright.png',
"size": (30,30)
},
"upcenter":{
"filename": 'upcenter.png',
"size": (30,30)
}
}
FOLDER = "./images/"
loaded = {}
def load(name):
"""
accède à l'image d'identifiant name.
si l'image n'est pas déjà dans loaded, l'ajoute.
renvoie l'image.
"""
assert name in IMAGES
if name not in loaded:
attributes = IMAGES[name]
image = pygame.image.load(FOLDER + attributes["filename"])
image = image.convert_alpha()
image = pygame.transform.scale(image, attributes["size"])
loaded['name'] = image
return loaded['name']
Vous voyez que le gestionnaire consiste en une liste d'identifiant qui permettent d'atteindre des noms de fichiers et des attributs de taille.
La variable loaded sert de cache (la mémoire contenant les chargement antérieurs)
La fonction consiste à vérifier si l'image demandée est déjà stockée dans loaded. Si ce n'est pas le cas, la fonction fait le chargement et stocke le résultat dans loaded de sorte qu'à la prochaine demande, le chargement sera inutile.
Une bonne part des sprites que nous devrons afficher sont des sprites fixes. C'est le cas des Coin et des Rectangle. À la création d'un Rectangle, nous devions spécifier des dimensions. Mais si le Rectangle est créé avec une image, l'image va donner les dimensions.
On se rend compte qu'on n'a plus tellement besoin de disposer de sprites de natures différentes. Ceci suffit :
# fixe.py
import pygame
import image
class Fixe(pygame.sprite.Sprite):
def __init__(self, x, y, png_name:str):
"""
x, y: position du coin supérieur gauche
png_name: nom du fichier png à utiliser
"""
super().__init__()
self.image = image.load(png_name)
self.rect = self.image.get_rect()
self.x = x
self.y = y
def adjust(self, camera):
"""
Ajuste la position de l'objet en accord avec la caméra
"""
self.rect.centerx = self.x - camera.x
self.rect.centery = self.y - camera.y
Qu'est-ce qui fait qu'un objet Fixe est une plate-forme ou autre chose ?
C'est le fait d'ajouter l'objet dans la groupe plateformes car c'est ce groupe qui est utilisé dans la détection de collision avec le joueur dans la méthode update_position de Player.
Remarquez que je fais le choix (dans adjust) de positionner l'objet par rapport à son centre ce qui sera mieux pour la plupart des objets comme les pièces.
Une pièce est un objet fixe mais avec un attribut valeur. Est-ce que ça vaut la peine de prévoir une classe rien que pour ça ?
Pas vraiment. On pourrait créer un Coin en tant qu'objet fixe puis attribuer l'attribut valeur.
Les considérations précédentes nous amènent au changement :
# level.py
import pygame
from fixe import Fixe
...
if car == 'P':
s = Fixe(x, y, 'upcenter')
plateformes.add(s)
elif car == 'D':
s = Fixe(x, y, 'dollar')
s.valeur = 20
coins.add(s)
...
Vous avez vu que j'ai prévu des images upleft et upright représentant des bords gauche ou droit de plates-formes. On pourrait modifier levelreader.py pour que, détectant les bords gauche ou droit de plates-formes, il emploie l'image adaptée parmi upleft, upcenter, upright.
En ayant fait ces choix, on peut jeter les modules coin.py et rectangle.py, ils ne serviront plus.
On commence à avoir des données éparpillées. Par exemple, il est écrit dans plateforme.py qu'un bout de plate-forme a la taille 30×30. Il est écrit aussi dans level.py que la case de base a un côté SIZE = 30.
La même info est donc stockée à deux endroits différents ce qui peut rendre le programme difficile à maintenir.
On pourra alors écrite un module data.py qui sera importé un peu partout et qui aura pour but de définir toutes les données utiles comme SIZE ou IMAGES.