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.
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.
IV. Explication du code▲
IV-A. Importation et Initialisation▲
Au niveau des importations, en général, on se contente de celles qui suivent :
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)
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.
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) où 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▲
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
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▲
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.
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▲
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.
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▲
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.
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▲
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.
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▲
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.
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) où 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())
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))
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
)