IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Oh Mummy


précédentsommairesuivant

I. Prérequis

Voici les langages et bibliothèques que j'ai utilisés pour développer Oh Mummy :

Système d'exploitation : Windows XP ;
Langage : Python 2.5 ;
Bibliothèque : Pygame 1.7.1.

II. Introduction

Mon objectif premier était de découvrir la bibliothèque Pygame. J'ai développé ce jeu sans aucune connaissance préalable de Pygame. Il m'a fallu une dizaine d'heures pour arriver à la version 1.0 qui ne doit plus à priori présenter de bogues. Vous pouvez donc utiliser ce document pour appréhender Pygame. De mon côté, je me suis avant tout servi du source aliens.py (disponible dans l'installation de Pygame) qui est très simple à comprendre pour apprendre les instructions de base du module Pygame.
Ainsi, j'ai essayé d'être le plus efficace possible sans forcément obtenir un code très propre (j'ai utilisé de nombreuses variables globales utiles dans tout le programme pour pouvoir y accéder très simplement). De plus, j'ai utilisé, je pense, les instructions minimales fournies par Pygame sans chercher forcément à en utiliser d'autres plus performantes.

Au niveau du graphisme, j'ai repris exactement le graphisme que l'on pouvait voir sur les CPC de l'époque en ayant avant tout créé toutes les images qui auraient besoin d'être incorporées dans le programme. En revanche, je n'ai pas intégré le son.

Image non disponible

III. Le jeu

Je pense que tout le monde connaît ce jeu et sait y jouer, ce n'est vraiment pas compliqué. Je vous laisse avec l'explication originelle.

Image non disponible

Image non disponible

Image non disponible

Image non disponible

Image non disponible

Image non disponible

Image non disponible

 

IV. Explication du code

IV-A. Importation et Initialisation

Au niveau des importations, en général, on se contente de celles qui suivent :

 
Sélectionnez
import pygame
import pygame.image
from pygame.locals import *
pygame.init()
pygame.font.init()

L'écran du jeu est créé par la fonction pygame.display.set_mode((largeur, hauteur)). On peut aussi changer le titre de la fenêtre par l'instruction pygame.display.set_caption(MonTitre)

 
Sélectionnez
screen = pygame.display.set_mode((768, 544))
pygame.display.set_caption("Oh Mummy")

IV-B. Chargement des images

Pour charger une image, il suffit d'appeler la fonction pygame.image.load(imagepath). J'ai décidé pour plus de clarté de charger toutes mes images dans un dictionnaire ImageOhMummy.

  • ImageOhMummy['Gargou'] contient les différentes positions que le héros peut prendre.
  • ImageOhMummy['Mummy'] contient les différentes positions qu'une momie peut prendre.
  • ImageOhMummy['GuardianMummy'] contient la séquence d'apparition de la momie cachée.
  • ImageOhMummy['Trace'] contient les différentes traces que peut laisser le héros sur le sol.
  • ImageOhMummy['Box'] contient les différentes découvertes que le héros peut faire.
  • ImageOhMummy['Chiffre'] contient les chiffres pour afficher le score.
  • ImageOhMummy['ChiffreScroll'] contient les chiffres pour afficher le score quand le héros possède le parchemin.
 
Sélectionnez
ImageOhMummy = {}
ImageOhMummy['Gargou'] = {}
ImageOhMummy['Gargou']['Left'] = pygame.image.load("./Image/GargouLeft.png")
...
ImageOhMummy['Mummy'] = {}
ImageOhMummy['Mummy']['Left'] = pygame.image.load("./Image/MummyLeft.png")
...
ImageOhMummy['GuardianMummy'] = {}
for i in range(1, 17):
    ImageOhMummy['GuardianMummy'][i] = pygame.image.load("./Image/GuardianMummy"+str(i)+".png")
...
ImageOhMummy['Trace'] = {}
ImageOhMummy['Trace']['None'] = pygame.image.load("./Image/TraceNone.png")
ImageOhMummy['Trace']['LeftDown'] = pygame.image.load("./Image/TraceLeftDown.png")
...
ImageOhMummy['Box'] = {}
ImageOhMummy['Box']['RoyalMummy'] = pygame.image.load("./Image/RoyalMummy.png")
...
ImageOhMummy['Chiffre'] = {}
ImageOhMummy['ChiffreScroll'] = {}
ImageOhMummy['Score'] = pygame.image.load("./Image/Score.png")
ImageOhMummy['Men'] = pygame.image.load("./Image/Men.png")
ImageOhMummy['EndLife'] = pygame.image.load("./Image/EndLife.png")
ImageOhMummy['End200'] = pygame.image.load("./Image/End200.png")
ImageOhMummy['GameOver'] = pygame.image.load("./Image/GameOver.png")
for i in range(10):
    ImageOhMummy['Chiffre'][i] = pygame.image.load('./Image/Chiffre'+str(i)+".png")
    ImageOhMummy['ChiffreScroll'][i] = pygame.image.load('./Image/ChiffreScroll'+str(i)+".png")

IV-C. Les différentes classes

IV-C-1. Base commune des classes

Les différentes classes présentées à la suite ont toutes une base commune. Tout d'abord, elles dérivent toutes de la classe pygame.sprite.Sprite. De ce que j'en ai compris, cette classe permet de définir un objet visuable à l'écran. La dérivation se fait de la manière suivante:
pygame.sprite.Sprite.__init__(self, self.containers)self.containers est un tuple contenant les groupes de sprites (dans notre cas des RenderUpdates (cf la fonction main())).

Dans la création de la classe, il y a ensuite deux attributs à définir obligatoirement:
L'attribut image contient l'image de votre objet. Il peut par exemple correspondre à une image préalablement chargée ou bien à une surface vide (pygame.Surface((largeur, hauteur))) que l'on pourra remplir avec la méthode fill(R, G, B) ou blit(img, (x, y)).
L'attribut rect définit en général le rectangle englobant image. On se contente en général de le créer de la façon suivante: self.rect = self.image.get_rect(). On pourra ensuite positionner ce rectangle dans la scène en modifiant par exemple les attributs top et left de rect.

Enfin, pour détruire un objet dérivant de la classe pygame.sprite.Sprite, il suffira de lui appliquer la méthode kill().

IV-C-2. Classe Gargou

Image non disponible

La classe Gargou permet de gérer le héros.
Les attributs x et y donne la position de héros dans le tableau représentant le plateau (cf la classe Plate). Le personnage prenant 4 cases de ce tableau, les composantes x et y correspondent à la position en haut à gauche du héros.
L'attribut facing donne le sens dans laquelle se déplace le héros (0: à droite, 1: en haut, 2: à gauche, 3: en bas). L'attribut moving gère les deux positions possibles pour chaque sens de déplacement.
La fonction move gère le déplacement du personnage en fonction de la flèche sur laquelle vous avez appuyé. La fonction vérifie tout d'abord la validité du déplacement. Chaque fois que le personnage effectue un déplacement valide, il laisse derrière lui une trace de pas qui va permettre de dégager les boîtes. Cela sera géré par l'appel à PLATE.Update(…). À chaque déplacement, en fonction de la valeur des attributs facing et moving, on met à jour l'attribut image par l'image correspondante.
Enfin, l'attribut level donne le niveau du héros et permet de définir l'algorithme de déplacement des momies. À chaque fois que le héros finit la série des 5 niveaux d'une pyramide, cet attribut augmente de 1.

Quand le héros a récupéré la clé et les restes de la momie, il peut changer de niveau en remontant à l'entrée initiale (condition à vérifier : PLATE.Key and PLATE.RoyalMummy and self.x == 15 and self.y == 1).
Une unique instance de cette classe est créée et est globale au jeu nommée GARGOU

 
Sélectionnez
class Gargou(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = ImageOhMummy['Gargou']['MoveDownRight']
        self.rect = self.image.get_rect()
        self.x = 15
        self.y = 1
        self.facing = 0
        self.moving = 0
        self.rect.left = 320
        self.rect.top = 94
        self.level = 1
        

    def move(self, direction):
        try: direct = direction.index(1)
        except: return

        self.facing = direct
            
        x = self.x; y = self.y 
        if direct == 0: x = self.x + 1
        elif direct == 1: y = self.y - 1
        elif direct == 2: x = self.x - 1
        elif direct == 3: y = self.y + 1

        if PLATE.plate[y][x] == PLATE.plate[y][x+1] == PLATE.plate[y+1][x] == PLATE.plate[y+1][x+1] == 1:
            self.x = x
            self.y = y
        else: return
        
        PLATE.Update(self.x, self.y, self.facing, self.moving)
        if PLATE.Key and PLATE.RoyalMummy and self.x == 15 and self.y == 1:
            PLATE.Level = max(1, (PLATE.Level + 1) % 6)
            PLATE.ChangeLevel = True
        self.moving = (self.moving + 1) % 2
        self.image = ImageOhMummy['Gargou']['Face'][2 * self.facing + self.moving]
        self.rect.left = 80 + 16 * self.x
        self.rect.top = 78 + 16 * self.y

IV-C-3. Classe Life

Image non disponible

La classe Life permet de gérer à la fois le nombre restant de vies du héros et son affichage à l'écran. La fonction Update permet de réafficher à l'écran les vies restantes du héros quand ce nombre change.
Une seule instance de cette classe sera créée en tant que variable globale et nommée LIFE pour une manipulation plus aisée.

 
Sélectionnez
class Life(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.life = 5  ## Le héros démarre avec 5 vies
        self.image = pygame.Surface((320, 32))
        self.rect = self.image.get_rect()
        self.rect.top = 78
        self.rect.left = 416

    def Update(self):
        self.image.fill((255, 255, 0))
        self.image.blit(ImageOhMummy['Men'], (0, 0))        
        for i in range(0, self.life, 2):
            self.image.blit(ImageOhMummy['Gargou']['MoveRight'], (64 + 32* i, 0))
        for i in range(1, self.life, 2):
            self.image.blit(ImageOhMummy['Gargou']['Right'], (64 + 32* i, 0))

IV-C-4. Classe Score

Image non disponible

La classe Score permet de gérer le score du héros ainsi que son affichage à l'écran. Il y a de même une unique fonction Update appelée chaque fois que le score change. Si le héros a découvert le parchemin et qu'il ne l'a pas encore utilisé, le score est affiché en inversant les couleurs jaune et bleu.
Une seule instance de cette classe sera créée en tant que variable globale et nommée SCORE pour une manipulation plus aisée.

 
Sélectionnez
class Score(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.score = 0
        self.image = pygame.Surface((176, 16))
        self.image.fill((255, 255, 0))
        self.rect = self.image.get_rect()
        self.rect.top = 78
        self.rect.left = 96
        self.image.blit(ImageOhMummy['Score'], (0, 0))
        for i in range(5):
            self.image.blit(ImageOhMummy['Chiffre'][0], (96 + 16* i, 0))
        
    def Update(self):
        scorestr = str(self.score).zfill(5)  ## Récupération du score sous forme d'une chaîne de caractères de longueur 5
        b = 0
        for i in scorestr:
            if PLATE.Scroll: self.image.blit(ImageOhMummy['ChiffreScroll'][int(i)], (96+16 * b, 0))
            else: self.image.blit(ImageOhMummy['Chiffre'][int(i)], (96+16 * b, 0))
            b += 1

IV-C-5. Classe Mummy

Image non disponible

La classe Mummy permet de gérer les momies (mouvement et affichage à l'écran). Toutes les momies du jeu sont contenues dans la liste globale MUMMY. Une instance de Mummy possède les mêmes attributs qu'une instance de Gargou à savoir x, y, facing et moving et ont la même signification.
L'attribut supplémentaire movable permet de gérer la vitesse de déplacement de la momie pour que celle-ci ne se déplace pas trop vite.

On distingue ici trois fonctions movevalable, move et Initialize.
La fonction Initialize permet de repositionner chaque momie encore vivante au changement de niveau.
La fonction movevalable retourne toutes les cases sur laquelle une momie peut se déplacer à partir de sa position.
La fonction move gère le déplacement de la momie. Ce déplacement va dépendre du niveau atteint par le héros. Les momies seront ainsi de plus en plus « intelligentes » et chercheront à force à se déplacer en direction du héros.

 
Sélectionnez
class Mummy(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = ImageOhMummy['Mummy']['MoveRight']
        self.rect = self.image.get_rect()
        self.x = x
        self.y = y
        self.facing = 0
        self.moving = 0
        self.rect.left = 80 + x * 16
        self.rect.top = 78 + y * 16
        self.movable = 0

    def movevalable(self):
        res = []
        if PLATE.plate[self.y][self.x+2] == 1 and PLATE.plate[self.y+1][self.x+2] == 1: res.append(0)
        if PLATE.plate[self.y-1][self.x] == 1 and PLATE.plate[self.y-1][self.x+1] == 1: res.append(1)
        if PLATE.plate[self.y][self.x-1] == 1 and PLATE.plate[self.y+1][self.x-1] == 1: res.append(2)
        if PLATE.plate[self.y+2][self.x] == 1 and PLATE.plate[self.y+2][self.x+1] == 1: res.append(3)
        return res

    def move(self):
        self.movable = (self.movable + 1 ) % 3
        if self.movable == 0:        
            if GARGOU.level == 1: direct = random.randint(0, 3)
            elif GARGOU.level == 2: direct = random.choice([0, 1, 2, 3, self.facing, self.facing])
            elif GARGOU.level == 3: direct = random.choice(self.movevalable())
            elif GARGOU.level == 4:
                m = self.movevalable()
                m.extend([self.facing, self.facing, self.facing])
                direct = random.choice(m)
            elif GARGOU.level == 5:
                m = []
                if self.x == GARGOU.x:
                    if self.y - GARGOU.y < 0: direct = 3
                    elif self.y - GARGOU.y > 0: direct = 1
                elif self.y == GARGOU.y:
                    if self.x - GARGOU.x < 0: direct = 0
                    elif self.x - GARGOU.x > 0: direct = 2
                else:
                    m = self.movevalable()
                    m.extend([self.facing, self.facing, self.facing])
                    direct = random.choice(m)
            elif GARGOU.level >= 6:
                m = []
                if self.x - GARGOU.x < 0: m.append(0)
                elif self.x - GARGOU.x > 0: m.append(2)
                if self.y - GARGOU.y < 0: m.append(3)
                elif self.y - GARGOU.y > 0: m.append(1)
                direct = random.choice(m)

                
            self.facing = direct
            
            x = self.x; y = self.y 
            if direct == 0: x = self.x + 1
            elif direct == 1: y = self.y - 1
            elif direct == 2: x = self.x - 1
            elif direct == 3: y = self.y + 1

            if PLATE.plate[y][x] == PLATE.plate[y][x+1] == PLATE.plate[y+1][x] == PLATE.plate[y+1][x+1] == 1:
                for mum in MUMMY:
                    if self != mum and abs(x-mum.x) <= 1 and abs(y -mum.y) <= 1:
                        return
                else:
                    self.x = x
                    self.y = y
            else: return
            self.moving = (self.moving + 1) % 2        
            self.image = ImageOhMummy['Mummy']['Face'][2 * self.facing + self.moving]
            self.rect.left = 80 + 16 * self.x
            self.rect.top = 78 + 16 * self.y

    def Initialize(self, x, y):
        self.x = x
        self.y = y
        self.facing = 0
        self.moving = 0
        self.rect.left = 80 + x * 16
        self.rect.top = 78 + y * 16
        self.movable = 0

IV-C-6. Classe GuardianMummy

Image non disponible

La classe GuardianMummy permet de gérer la naissance de la momie cachée dans une des boîtes. Sa position dans sa boîte va dépendre de la position du héros au moment où celui-ci a dégagé entièrement la boîte de telle sorte que cette position soit la plus proche de la position du dégagement. Là encore, il n'y a qu'une fonction utile, la fonction Update. Celle-ci va gérer l'apparition régulière de la momie.
Il y a 16 vues avant que la momie soit entièrement apparue. Une fois ce nombre atteint, on rajoute donc une momie dans la liste globale MUMMY. la case de naissance de la momie devenant une case sur laquelle le héros et les autres momies peuvent se déplacer, on met à jour le tableau PLATE.plate. Enfin, on vide la liste globale GUARDIANMUMMY (qui ne peut contenir au maximum qu'un élément) et on tue l'instance de la classe GuardianMummy.

 
Sélectionnez
class GuardianMummy(pygame.sprite.Sprite):
    def __init__(self, x, y):
        ## x et y corresponde au numéro de la boîte sachant que 0<=x<=4 et 0<=y<=3
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.view = 0
        self.image = pygame.Surface((0,0))  ## On part d'une image vide
        self.rect = self.image.get_rect()
        ## Définition de la position de naissance de la Momie
        if GARGOU.x < 3 + 7 * x: self.x = 3 + 7 * x
        elif GARGOU.x > 6 + 7 * x: self.x = 6 + 7 * x
        else: self.x = GARGOU.x
        if GARGOU.y < 5 + 5 * y: self.y = 5 + 5 * y
        elif GARGOU.y > 6 + 5 * y: self.y = 6 + 5 * y
        else: self.y = GARGOU.y
        self.movable = 0
            

    def Update(self):
        self.movable = (self.movable + 1 ) % 6
        if self.movable == 0:
            self.view += 1
            self.image = ImageOhMummy['GuardianMummy'][self.view]
            self.rect = self.image.get_rect()
            self.rect.top = 110 + 16 * self.y - 2 * self.view
            self.rect.left = 80 + 16 * self.x
            
            if self.view == 16:
                MUMMY.append(Mummy(self.x, self.y))
                PLATE.plate[self.y][self.x] = 1
                PLATE.plate[self.y+1][self.x] = 1
                PLATE.plate[self.y][self.x+1] = 1
                PLATE.plate[self.y+1][self.x+1] = 1
                PLATE.image.blit(ImageOhMummy['Trace']['None'], (16 * self.x - 16, 16 * self.y - 16))
                PLATE.image.blit(ImageOhMummy['Trace']['None'], (16 * (self.x+1) - 16, 16 * self.y - 16))
                PLATE.image.blit(ImageOhMummy['Trace']['None'], (16 * self.x - 16, 16 * (self.y+1) - 16))
                PLATE.image.blit(ImageOhMummy['Trace']['None'], (16 * (self.x+1) - 16, 16 * (self.y+1) - 16))                
                GUARDIANMUMMY.pop()                
                self.kill()

IV-C-7. Classe Plate

Image non disponible

La classe Plate gère le plateau de jeu.
L'attribut plate est un tableau à deux dimensions représentant les cases possibles de déplacement, 1 étant une case accessible et 0 une non accessible (bord ou une partie d'une boîte). Comme on le voit sur la figure ci-dessus, les cases jaunes représentent une partie d'un mur, les cases blanches une partie d'une boîte et les cases noires représentent le chemin sur lequel peut mouvoir le héros.
L'attribut trace est un tableau à deux dimensions donnant les cases sur lesquelles le héros s'est déjà déplacé et permettant de savoir si une boîte a été entièrement dégagée. Il a donc les mêmes dimensions que l'attribut plate. L'attribut box est un tableau à deux dimensions donnant l'état des boîtes à découvrir (au départ toutes sont à NonTested).
L'attribut boxchoice est un tableau à 1 dimension de même taille que box qui contient toutes les découvertes à faire, chaque découverte étant aléatoirement reliée à une case de box.
L'attribut Key détermine si la clé a été découverte.
L'attribut Scroll détermine si le héros possède le parchemin.
L'attribut RoyalMummy détermine si les restes de la momie ont été découverts.
L'attribut Level détermine le niveau dans la pyramide dans lequel évolue le héros (1 <= Level <= 5).
L'attribut ChangeLevel indique si le héros doit changer de niveau dans la pyramide.

Une case du plateau de jeu représente une surface de 16x16 à l'écran. Ainsi les personnages prennent 2x2 cases sur le jeu. Une boîte contient 5x3 cases. On se retrouve ainsi avec un total de 39x26 cases sachant que la bordure du tableau plate représente le mur extérieur (d'épaisseur une case). Il y a ainsi toute une gymnastique pour se repérer à l'intérieur de ce tableau.
Chaque fois qu'on initialise le plateau (c'est-à-dire qu'on change de niveau), une momie supplémentaire apparaît (MUMMY.append(Mummy(1, 23))) que l'on place en bas à gauche de l'écran, les momies survivantes sont repositionnées à côté.

La fonction Update (appelée lors du mouvement du héros) permet de mettre à jour et d'afficher le tableau des traces de pas puis appelle la fonction UpdateBox pour vérifier si une boîte a été ou non entièrement dégagée. Celle-ci appelle elle-même la fonction UpdateClose en spécifiant la boîte test. Si une boîte est dégagée, on effectue le résultat de la boîte (augmentation du score, apparition d'une momie, découverte d'un objet…).
De même que la plupart des autres classes, une seule instance globale est créée et nommée PLATE, ce qui permet d'y avoir accès facilement dans les autres classes.

 
Sélectionnez
class Plate(pygame.sprite.Sprite):
    def __init__(self, level):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.Initialize(level)

    def Initialize(self, level):
        self.plate = [39 * [0] for i in range(26)]
        self.trace = [39 * [0] for i in range(26)]
        self.box = [5 * ['NonTested'] for i in range(4)]
        l = str(level)
        self.boxchoice = ['Treasure', 'Treasure', 'Treasure', 'Treasure', 'Treasure', 'Treasure', 'Treasure', 'Treasure', 'Treasure', 'Treasure',
                           'GuardianMummy'+l, 'Key', 'RoyalMummy', 'Scroll', 'Empty'+l, 'Empty'+l, 'Empty'+l, 'Empty'+l, 'Empty'+l, 'Empty'+l]
        random.shuffle(self.boxchoice)

        self.Key = False
        self.Scroll = False
        self.RoyalMummy = False
        self.Level = level
        self.ChangeLevel = False
            
        
        for i in range(3, 24, 5):
            for j in range(1, 38):
                self.plate[i][j] = 1
                self.plate[i+1][j] = 1
        for j in range(1, 37, 7):
            for i in range(3, 25):
                self.plate[i][j] = 1
                self.plate[i][j+1] = 1                
        self.plate[1][15] = 1
        self.plate[1][16] = 1
        self.plate[2][15] = 1
        self.plate[2][16] = 1

        self.image = pygame.Surface((592, 384))
        self.image.fill((255, 255, 0))
        self.rect = self.image.get_rect()
        self.rect.left = 96
        self.rect.top = 94


        ##  Affichage du plateau
        for i in range(1, 25):
            for j in range(1, 38):
                if self.plate[i][j] == 1:
                    self.image.blit(ImageOhMummy['Trace']['None'], (16 * j - 16, 16 * i - 16))

        for i in range(4):
            for j in range(5):
                self.image.blit(ImageOhMummy['Box']['NonTested'+l], (32 + 16 * 7 * j, 64 + 16 * 5 * i))
        LIFE.Update()
        MUMMY.append(Mummy(1, 23))
        i = 1
        for mum in MUMMY:
            mum.Initialize(1 + 2 * i, 23)
            i += 1

    def Update(self, x, y, facing, moving):
        ## Mise à jour des traces
        if facing == 0:
            self.trace[y][x-1] = 1
            self.trace[y+1][x-1] = 1
            if moving == 0: self.image.blit(ImageOhMummy['Trace']['DownRight'], (16 * x - 32, 16 * y - 16))
            else: self.image.blit(ImageOhMummy['Trace']['UpRight'], (16 * x - 32, 16 * y - 16))
        elif facing == 1:
            self.trace[y+2][x] = 1
            self.trace[y+2][x+1] = 1
            if moving == 0: self.image.blit(ImageOhMummy['Trace']['LeftUp'], (16 * x - 16, 16 * y + 16))
            else: self.image.blit(ImageOhMummy['Trace']['RightUp'], (16 * x - 16, 16 * y + 16))
        elif facing == 2:
            self.trace[y][x+2] = 1
            self.trace[y+1][x+2] = 1
            if moving == 0: self.image.blit(ImageOhMummy['Trace']['DownLeft'], (16 * x + 16 , 16 * y - 16))
            else: self.image.blit(ImageOhMummy['Trace']['UpLeft'], (16 * x + 16, 16 * y - 16))
        elif facing == 3:
            self.trace[y-1][x] = 1
            self.trace[y-1][x+1] = 1
            if moving == 0: self.image.blit(ImageOhMummy['Trace']['LeftDown'], (16 * x - 16, 16 * y - 32))
            else: self.image.blit(ImageOhMummy['Trace']['RightDown'], (16 * x - 16 , 16 * y - 32 ))            

        ## Mise à jour des cases
        if y>2: self.UpdateBox(x, y)

    def UpdateBox(self, x, y):
        xx1 = min(4, max(0, (x - 3) // 7))
        yy1 = min(3, max(0, (y - 5) // 5))
        xx2 = min(4, (x - 1 )// 7)
        yy2 = min(3, (y - 3) // 5)
        if self.box[yy1][xx1] == 'NonTested': self.UpdateClose(xx1, yy1)
        if self.box[yy1][xx2] == 'NonTested': self.UpdateClose(xx2, yy1)
        if self.box[yy2][xx1] == 'NonTested': self.UpdateClose(xx1, yy2)
        if self.box[yy2][xx2] == 'NonTested': self.UpdateClose(xx2, yy2)

    def UpdateClose(self, x, y):
        for i in range(3 + 5 * y, 10 + 5 * y):
            for j in range(1 + 7 * x, 10 + 7 * x):
                if self.plate[i][j] == 1 and self.trace[i][j] == 0: return False
        self.box[y][x] = self.boxchoice[5 * y + x]
        self.image.blit(ImageOhMummy['Box'][self.box[y][x]], (32 + 112 * x, 64 + 80 * y))
        if self.box[y][x] == 'Treasure': SCORE.score += 5
        elif self.box[y][x] == "RoyalMummy":
            SCORE.score +=50
            self.RoyalMummy = True
        elif self.box[y][x] == "Scroll":
            self.Scroll = True
            SCORE.Update()
        elif self.box[y][x] == "Key":
            self.Key = True
        elif self.box[y][x] == "GuardianMummy"+str(self.Level):
            GUARDIANMUMMY.append(GuardianMummy(x, y))
        SCORE.Update()

IV-D. Fonction main()

La fonction main va nous permettre d'initialiser toutes nos variables et de gérer les évènements clavier et le rafraîchissement de l'écran.

clock = pygame.time.Clock() permet de créer une horloge qui permettra ensuite de fixer le nombre d'images à la seconde que générera le jeu. Ce nombre est alors défini par la fonction clock.tick(fps)fps est le nombre d'images par seconde.

On définit ensuite les différents groupes de sprite (all, plate, endgame) de type ici pygame.sprite.RenderUpdates qui géreront l'affichage à l'écran, lesquels sont attribués aux différentes classes (Plate.containers = plate, Score.containers = all…). Enfin nos différentes variables sont créées et définies de manière globale. Puis on affiche un arrière-plan sur l'écran de jeu ( screen.blit(background, (0, 0))) et on force le rafraîchissement entier de la fenêtre de jeu (pygame.display.flip())

 
Sélectionnez
def main():
    clock = pygame.time.Clock()

    background = pygame.Surface(SCREENRECT.size)
    background = background.convert()
    background.fill((255,255,0))

    all = pygame.sprite.RenderUpdates()
    plate = pygame.sprite.RenderUpdates()
    endgame = pygame.sprite.RenderUpdates()
    Plate.containers = plate
    Score.containers = all
    Gargou.containers = all
    Life.containers = all
    Mummy.containers = all
    GuardianMummy.containers = all
    global PLATE
    global SCORE
    global GARGOU
    global LIFE
    global MUMMY
    global GUARDIANMUMMY
    
    SCORE = Score()
    LIFE = Life()
    MUMMY = []
    GUARDIANMUMMY = []
    GARGOU = Gargou()
    GARGOU.level = 1
    PLATE = Plate(1)
    
    
    screen.blit(background, (0, 0))
    pygame.display.flip()

On définit enfin une boucle infinie dans laquelle sera contrôlé le jeu.
Cette boucle commence par une condition qui teste les évènements reçus par Pygame (pygame.event.get()). Si le joueur a appuyé sur la touche Escape (évènement de type KEYDOWN) ou bien qu'un évènement de type QUIT a été généré, on quitte le jeu.

On effectue alors les actions des différents personnages :
- on récupère les touches sur lesquelles le joueur a appuyé en ne considérant que les flèches et on effectue les déplacements du héros (GARGOU.move(direction)) ;
- si une momie est en train de naître, guardian.Update() met à jour son évolution ;
- pour chaque momie du jeu, on effectue un déplacement (mum.move()). Puis on vérifie qu'une fois déplacée, elle n'entre pas en collision avec le héros. Si le héros perd sa dernière vie, on affiche l'image GameOver ( screen.blit(ImageOhMummy['GameOver'], (208, 238)) ) et on attend que le joueur quitte le jeu ou recommence une partie en appuyant sur la touche 'c' ou 'C'.

Si le joueur a fini un tableau (condition PLATE.ChangeLevel à vérifier), on réinitialise le tout.

Enfin, à la fin de la boucle, on réaffiche en dessinant nos groupes de sprite sur la scène (pl = plate.draw(screen) et dirty = all.draw(screen) ) et en effectuant le rafraîchissement à l'écran (pygame.display.update(pl) et pygame.display.update(dirty)), le tout en forçant le nombre de frames par seconde (clock.tick(12))

 
Sélectionnez
    goon = True
    while goon:
        for event in pygame.event.get():
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): return


        keystate = pygame.key.get_pressed()
        direction = [keystate[K_RIGHT], keystate[K_UP], keystate[K_LEFT], keystate[K_DOWN]]
        
        GARGOU.move(direction)
        for guardian in GUARDIANMUMMY: guardian.Update()
        for mum in MUMMY:
            mum.move()
            if abs(mum.x - GARGOU.x) <= 1 and abs(mum.y - GARGOU.y) <= 1:
                if not PLATE.Scroll:
                    LIFE.life -= 1
                    LIFE.Update()
                    if LIFE.life == 0:
                        goon = False
                        screen.blit(ImageOhMummy['GameOver'], (208, 238))
                        while True:
                            for event in pygame.event.get():
                                if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):  return                    
                            keystate = pygame.key.get_pressed()
                            if keystate[ord('c')] or keystate[ord('C')]:
                                return main()
                            pygame.display.flip()
                            clock.tick(10)                        
                else:
                    PLATE.Scroll = False
                    SCORE.Update()
                mum.kill()
                MUMMY.remove(mum)
        

        if PLATE.ChangeLevel:
            if PLATE.Level == 1:
                if random.randint(0, 1) == 0 and LIFE.life < 9:
                    background.blit(ImageOhMummy['EndLife'], (0, 0))
                    LIFE.life += 1
                else:
                    background.blit(ImageOhMummy['End200'], (0, 0))
                    SCORE.score += 200

                screen.blit(background, (0, 0))
                for mum in MUMMY: mum.kill()
                MUMMY = []
                GARGOU.level += 1

               
                while True:
                    for event in pygame.event.get():
                        if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): return
                    keystate = pygame.key.get_pressed()
                    if keystate[ord('c')] or keystate[ord('C')]: break
                    pygame.display.flip()
                    clock.tick(10)

            for mum in GUARDIANMUMMY: mum.kill()
            GUARDIANMUMMY = []
            background.fill((255,255,0))
            screen.blit(background, (0, 0))
            pygame.display.flip()
             
            PLATE.Initialize(PLATE.Level)
            SCORE.Update()
            
        #draw the scene
        pl = plate.draw(screen)
        dirty = all.draw(screen)
        
        pygame.display.update(pl)
        pygame.display.update(dirty)

        clock.tick(12)
    pygame.time.wait(1000)

précédentsommairesuivant

Copyright © 2007 Guillaume Duriaud. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.