====== Gestionnaire d'images ====== 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 : {{ :nsi:tds:pygame:plateforme:images.zip |}}. Ainsi toutes les images seront bien rangées. ===== Le gestionnaire ===== 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. ===== sprite fixe ===== 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. ===== Et les Coins ===== 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''. ===== level.py ===== 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. ===== data ===== 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 30x30. 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''.