Menu de sélection des niveaux
Pour que notre jeu soit plus complet, on veut proposer une selection de niveaux, que le joueur peut choisir à partir d'un menu.
Principe général
On veut donner à notre jeu le comportement suivant :
Le joueur lance le jeu.
Une liste des niveaux apparaît à l'écran.
Le joueur fait défiler les niveaux en utilisant les flèches haut et bas. Il lance un niveau en appuyant sur R
.
Le joueur joue son niveau. Si il appuie sur ECHAP
, il retourne dans le menu.
Si il appuie sur ECHAP
dans le menu, celà quitte le jeu.
TODO (prof) schema ici
Lister les niveaux
Il nous faut lister les niveaux dans le répertoire où se trouve le programme.
Pour ça, on peut utilier la fonction listdir
du module os
de Python, qui nous permet de lister toutes les entrées d'un répertoire (c'est à dire les fichiers et les dossiers). Le module os
est déjà importé depuis l'étape précédente, on n'a pas besoin de le réimporter.
entrees
contient une liste de noms de fichiers présents dans le répertoire. Malheureusement, celà inclut le script courant, et les autres fichiers ou répertoires que l'on aurait pu rajouter. On peut utiliser la syntaxe des listes par compréhension pour selectionner uniquement les fichiers qui contiennent ".lasero"
.
## On crée une liste de tous les éléments f de entrees qui vérifient ".lasero" in f
fichiers_niveaux = [f for f in entrees if ".lasero" in f]
Finalement, retire son extension à chaque nom de fichier.
En combinant ces opérations, on crée la fonction lister_niveaux
.
def lister_niveaux():
entrees = os.listdir(os.path.dirname(__file__))
fichiers_niveaux = [f for f in entrees if ".lasero" in f]
noms_niveaux = []
for f in fichiers_niveaux:
noms_niveaux.append(f.replace(".lasero", ""))
return noms_niveaux
Charger les niveaux
On veut maintenant charger la totalité des niveaux en mémoire, et se rappeler de leur noms respectifs.
On commence donc par ajouter un attribut nom
dans la classe Niveau
.
class Niveau:
def __init__(self, nom, personnage, decompte_victoire, projectiles):
self.nom = nom
self.personnage = personnage
self.decompte_victoire = decompte_victoire
self.projectiles = projectiles
On modifie la fonction charger_niveau
pour qu'elle renseigne cet attribut et qu'elle prenne directement le nom du niveau en paramètre.
def charger_niveau(nom):
with open(chemin_vers_niveau(nom), "r") as f:
lignes = f.readlines()
personnage = analyse_personnage(lignes[0])
decompte_victoire = analyse_decompte_victoire(lignes[1])
projectiles = []
for l in lignes[2:]:
projectiles.append(analyse_projectile(l))
return Niveau(nom, personnage, decompte_victoire, projectiles)
On l'utilise en combinaison avec lister_niveaux
pour écrire la fonction charger_niveaux
, qui renvoie la liste de tous les niveaux.
def charger_niveaux():
noms_niveaux = lister_niveaux()
niveaux=[]
for n in noms_niveaux:
niveaux.append(charger_niveau(n))
return niveaux
Principe du menu
On choisit d'afficher le menu comme suit :
Le nom du niveau actuellement sélectionnée est en blanc au centre de l'écran.
Les deux niveaux précédents dans la liste, si ils existent, sont affichés centrés mais décalés vers le haut, avec un blanc plus transparent en fonction de la distance.
Les deux niveaux suivants dans la liste, si ils existent, sont affichés centrés mais décalés vers le bas, avec un blanc plus transparent en fonction de la distance.
TODO (prof) visuel ici
Classe menu
Commençont par créer une classe qui contient toutes les informations du menu.
On la met dans une section Menu
, qui accueillera également les fonctions de menu.
####################
#### MENU ####
####################
class Menu:
def __init__(self, niveaux, ecran):
# Ces 4 booléens sont mis a True
# si le joueur a lancé la commande dans la frame
self.haut = False
self.bas = False
self.lancer = False
self.quitter = False
self.niveaux = niveaux
#indice du niveau sélectionné.dans la liste des niveaux
self.niveau_selectionne = 0
self.ecran = ecran
Boucle principale du menu
La boucle principale du menu ressemble beaucoup à la boucle principale du jeu.
def lancer_menu(niveaux, ecran):
menu = Menu(niveaux, ecran)
clock = pygame.time.Clock()
while not menu.quitter:
clock.tick(FPS)
traiter_controles_menu(menu)
selectionner_niveau_menu(menu)
lancer_niveau_menu(menu)
afficher_menu(menu)
Il nous reste simplement à implémenter ces fonctions.
Controles
Pour les contrôles du menu, on va utiliser les même controles que le jeu. On ne crée donc pas de nouvelle constantes.
def traiter_controles_menu(menu):
## on remet à zéro les contrôles pour ne pas reprendre
## l'état de la frame précédente
menu.haut = False
menu.bas = False
menu.lancer = False
menu.quitter = False
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == TOUCHE_QUITTER:
menu.quitter = True
if e.key == TOUCHE_RECOMMENCER:
menu.lancer = True
if e.key == TOUCHE_HAUT:
menu.haut = True
if e.key == TOUCHE_BAS:
menu.bas = True
Selection du niveau
Pour selectionner le niveau, on va modifier menu.niveau_selectionne
en fonction des commandes menu.haut
et menu.bas
.
def selectionner_niveau_menu(menu):
if menu.haut:
menu.niveau_selectionne -= 1 #on remonte dans la liste
if menu.bas:
menu.niveau_selectionne += 1 #on descends dans la liste
#on vérifie qu'on ne sort pas de la liste
if menu.niveau_selectionne < 0:
menu.niveau_selectionne = 0
if menu.niveau_selectionne > len(menu.niveaux) - 1:
menu.niveau_selectionne = len(menu.niveaux) - 1
Lancement du niveau
Le lancement du jeu est simple. Si la commande menu.lancer
est active, on lance la partie avec jouer_niveau
, sur le niveau sélectionné.
TODO (prof) reprendre ici (ci dessus)
def lancer_niveau_menu(menu):
if menu.lancer:
jouer_niveau(menu.niveaux[menu.niveau_selectionne], menu.ecran)
Centrer et décaler une image
Pour afficher le menu, on a besoin de dessiner une image centrée avec un éventuel décalage vers le haut ou le bas.
C'est pas très compliqué : on reprends la fonction de dessin d'une image centrée qu'on avait écrite au moment de l'écran de fin, et on en fait une copie qui permet de rajouter un tel décalage sous forme d'un vecteur. On ajoute simplement ce vecteur à la position centrée de l'image.
TODO (prof) ajouter schema ici
En Python, ça donne :
def rendre_image_centree_decalee(ecran, image, decalage):
centre_ecran = Vector2(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/2)
centre_image = Vector2(image.get_width()/2, image.get_height()/2)
ecran.blit(image, centre_ecran - centre_image + decalage)
Fonte et texte
On doit encore une fois charger une fonte. On renseigne la taille et sa couleur dans une constante. On construit un texte par niveau dans la classe menu.
class Menu:
def __init__(self, niveaux, ecran):
# Ces 4 booléens sont mis a True
# si le joueur a lancé la commande dans la frame
self.haut = False
self.bas = False
self.lancer = False
self.quitter = False
self.niveaux = niveaux
#indice du niveau sélectionné.dans la liste des niveaux
self.niveau_selectionne = 0
self.ecran = ecran
self.textes_niveaux = {}
fonte = pygame.font.SysFont("Mono", TAILLE_MENU, True, False)
for n in niveaux:
self.textes_niveaux[n.nom] = fonte.render(n.nom, True, COULEUR_MENU)
Affichage
On spécifie l'espacement attendu et le gain de transparence entre deux lignes du menu dans des constantes.
La transparence est souvent appelée canal alpha dans les images. Une valeur d'alpha de 255 équivaut à une couleur opaque, alors que une valeur de 0 représente une couleur transparente.
Dans la fonction d'affichage du menu, après avoir effacé l'écran, on trace le texte du niveau sélectionné, et si besoin des niveaux avant et après dans la liste.
TODO (prof) ici besoin de plus d'explications ?
On crée une fonction intermédiaire pour afficher le nom d'un niveau d'index i, avec un décalage et une transparence.
def rendre_nom_niveau(menu, i, decalage, transparence):
ecran = menu.ecran
texte = menu.textes_niveaux[menu.niveaux[i].nom]
texte.set_alpha(transparence) #on décide de la transparence
rendre_image_centree_decalee(ecran, texte, Vector2(0, decalage))
def afficher_menu(menu):
ecran = menu.ecran
ecran.fill(COULEUR_ARRIERE_PLAN)
n = menu.niveau_selectionne
rendre_nom_niveau(menu, n, 0, 255)
# On dessine les niveaux adjacents si besoin,
# en ajoutant un décalage de position
if n > 1:
rendre_nom_niveau(menu, n - 2, -ESPACEMENT_MENU*2, 255 - GAIN_TRANSPARENCE_MENU * 2)
if n > 0:
rendre_nom_niveau(menu, n - 1, -ESPACEMENT_MENU, 255 - GAIN_TRANSPARENCE_MENU)
if n < len(menu.niveaux) - 1:
rendre_nom_niveau(menu, n + 1, ESPACEMENT_MENU, 255 - GAIN_TRANSPARENCE_MENU)
if n < len(menu.niveaux) - 2:
rendre_nom_niveau(menu, n +2, ESPACEMENT_MENU*2, 255 - GAIN_TRANSPARENCE_MENU * 2)
pygame.display.flip()
Fonction main
On n'a plus qu'à éditer la fonction main pour qu'elle charge les niveaux et lance le menu.
def main():
pygame.init()
fenetre = pygame.display.set_mode([LARGEUR_FENETRE, HAUTEUR_FENETRE])
niveaux = charger_niveaux()
lancer_menu(niveaux, fenetre)
pygame.quit()
Si on lance le jeu, on peut voir que le menu nous propose le niveau "niveau", puisque c'est le seul que l'on a actuellement.
TODO (prof) capture ici
Le code complet de l'étape se trouve dans le dépliant ci-dessous
Code Complet
import pygame
from pygame.math import Vector2
import os
####################
#### CONFIG ####
####################
## Général
NIVEAU = "niveau"
FPS = 60
LARGEUR_FENETRE = 1280
HAUTEUR_FENETRE = 720
## Controles
TOUCHE_QUITTER = pygame.K_ESCAPE
TOUCHE_HAUT = pygame.K_UP
TOUCHE_BAS = pygame.K_DOWN
TOUCHE_DROITE = pygame.K_RIGHT
TOUCHE_GAUCHE = pygame.K_LEFT
TOUCHE_RECOMMENCER = pygame.K_r
## Simulation
VITESSE_PERSONNAGE = 300
TAILLE_PERSONNAGE = 10
DECOMPTE_VICTOIRE = 5.0
## Rendu
COULEUR_ARRIERE_PLAN = (0, 0, 0)
COULEUR_PERSONNAGE = (255, 146, 205)
COULEUR_PROJECTILES = (146, 255, 205)
COULEUR_CHRONOMETRE = (150, 150, 170)
TAILLE_CHRONOMETRE = 30
MARGES_CHRONOMETRE = 20
TAILLE_FIN_DE_PARTIE = 150
COULEUR_FIN_DE_PARTIE = (150, 150, 170)
## Menu
TAILLE_MENU = 50
COULEUR_MENU = (255, 255, 255)
ESPACEMENT_MENU = 120
GAIN_TRANSPARENCE_MENU = 100
####################
#### GENERAL ####
####################
class GameState :
def __init__(self):
self.controles = None # on met a None parce que ces attributs seront initialisés plus tard
self.simulation = None
self.rendu = None
####################
#### CONTROLEUR ####
####################
class Controles :
def __init__(self):
self.quitter = False
self.direction = Vector2(0,0)
self.recommencer = False
def initialiser_controles(etat):
etat.controles = Controles()
def reinitialiser_controles(etat):
etat.controles = Controles()
def traiter_controles(etat, t):
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == TOUCHE_QUITTER:
etat.controles.quitter = True
if e.key == TOUCHE_RECOMMENCER:
etat.controles.recommencer = True
direction = Vector2(0, 0)
clavier = pygame.key.get_pressed()
if clavier[TOUCHE_HAUT]:
direction.y += 1.0
if clavier[TOUCHE_BAS]:
direction.y -= 1.0
if clavier[TOUCHE_DROITE]:
direction.x += 1.0
if clavier[TOUCHE_GAUCHE]:
direction.x -= 1.0
if direction.length() != 0:
direction.normalize_ip()
etat.controles.direction = direction
####################
#### SIMULATION ####
####################
class Personnage:
def __init__(self, position):
self.position = position
def copier_personnage(personnage):
return Personnage(personnage.position.copy())
class Projectile:
def __init__(self, position, taille, direction, vitesse):
self.position = position
self.direction = direction
self.taille = taille
if self.direction.length != 0:
self.direction.normalize_ip()
self.vitesse = vitesse
def copier_projectile(projectile):
return Projectile(projectile.position.copy(), projectile.taille, projectile.direction.copy(), projectile.vitesse)
def copier_projectiles(projectiles):
copie = []
for p in projectiles:
copie.append(copier_projectile(p))
return copie
class Niveau:
def __init__(self, nom, personnage, decompte_victoire, projectiles):
self.nom = nom
self.personnage = personnage
self.decompte_victoire = decompte_victoire
self.projectiles = projectiles
class Simulation:
def __init__(self, niveau):
self.personnage = copier_personnage(niveau.personnage)
self.projectiles = copier_projectiles(niveau.projectiles)
self.collision = False
self.decompte_victoire = niveau.decompte_victoire
self.attente = True
def initialiser_simulation(etat, niveau):
etat.simulation = Simulation(niveau)
def reinitialiser_simulation(etat, niveau):
initialiser_simulation(etat, niveau)
def intersection(c1, r1, c2, r2):
return c1.distance_to(c2) <= r1 + r2
def detecter_collisions(etat):
perso = etat.simulation.personnage
for p in etat.simulation.projectiles:
if intersection(perso.position, TAILLE_PERSONNAGE, p.position, p.taille):
etat.simulation.collision = True
def deplacer_personnage(etat, t):
direction = etat.controles.direction
etat.simulation.personnage.position += direction * VITESSE_PERSONNAGE * t
def limiter_deplacement_personnage(etat):
p = etat.simulation.personnage
if p.position.x < TAILLE_PERSONNAGE:
p.position.x = TAILLE_PERSONNAGE
if p.position.x > LARGEUR_FENETRE - TAILLE_PERSONNAGE:
p.position.x = LARGEUR_FENETRE - TAILLE_PERSONNAGE
if p.position.y < TAILLE_PERSONNAGE:
p.position.y = TAILLE_PERSONNAGE
if p.position.y > HAUTEUR_FENETRE - TAILLE_PERSONNAGE:
p.position.y = HAUTEUR_FENETRE - TAILLE_PERSONNAGE
def deplacer_projectiles(etat, t):
for p in etat.simulation.projectiles:
p.position += p.direction * p.vitesse * t
def faire_rebondir_projectiles(etat):
for p in etat.simulation.projectiles:
if p.position.x < p.taille:
p.position.x = p.taille
p.direction.x *= -1
if p.position.x > LARGEUR_FENETRE - p.taille:
p.position.x = LARGEUR_FENETRE - p.taille
p.direction.x *= -1
if p.position.y < p.taille:
p.position.y = p.taille
p.direction.y *= -1
if p.position.y > HAUTEUR_FENETRE - p.taille:
p.position.y = HAUTEUR_FENETRE - p.taille
p.direction.y *= -1
def decompter_victoire(etat, t):
etat.simulation.decompte_victoire -= t
if etat.simulation.decompte_victoire < 0:
etat.simulation.decompte_victoire = 0.0
def verifier_attente(etat):
if etat.simulation.attente and etat.controles.direction.length() != 0.0:
etat.simulation.attente = False
def avancer_simulation(etat, t):
if etat.simulation.attente:
verifier_attente(etat)
elif not etat.simulation.collision and etat.simulation.decompte_victoire != 0.0:
deplacer_personnage(etat, t)
limiter_deplacement_personnage(etat)
deplacer_projectiles(etat, t)
faire_rebondir_projectiles(etat)
detecter_collisions(etat)
decompter_victoire(etat, t)
####################
#### RENDU ####
####################
def images_caracteres(fonte, caracteres, couleur):
cars = {}
for c in caracteres:
cars[c] = fonte.render(c, True, couleur)
return cars
class Rendu:
def __init__(self, ecran):
self.ecran = ecran
fonte_chrono = pygame.font.SysFont("Mono", TAILLE_CHRONOMETRE, True, False)
self.caracteres_chrono = images_caracteres(fonte_chrono, "0123456789.", COULEUR_CHRONOMETRE)
fonte_fin = pygame.font.SysFont("Mono", TAILLE_FIN_DE_PARTIE, True, False)
self.texte_victoire = fonte_fin.render("VICTOIRE", True, COULEUR_FIN_DE_PARTIE)
self.texte_defaite = fonte_fin.render("DEFAITE", True, COULEUR_FIN_DE_PARTIE)
def ref_sve(v):
return Vector2(v.x, HAUTEUR_FENETRE - v.y)
def initialiser_rendu(etat, ecran):
etat.rendu = Rendu(ecran)
def reinitialiser_rendu(etat):
pass
def rendre_personnage(etat):
ecran = etat.rendu.ecran
position = etat.simulation.personnage.position
pygame.draw.circle(ecran, COULEUR_PERSONNAGE, ref_sve(position), TAILLE_PERSONNAGE)
def rendre_projectiles(etat):
ecran = etat.rendu.ecran
for p in etat.simulation.projectiles:
position = p.position
pygame.draw.circle(ecran, COULEUR_PROJECTILES, ref_sve(position), p.taille)
def rendre_chronometre(etat):
chrono = f"{etat.simulation.decompte_victoire:06.2f}" #Conversion en chaine
caracteres = etat.rendu.caracteres_chrono
x = MARGES_CHRONOMETRE #x de départ (référentiel écran)
y = MARGES_CHRONOMETRE #y de départ (référentiel écran)
for c in chrono:
etat.rendu.ecran.blit(caracteres[c], (x, y))
#la methode get_width de l'objet image renvoie la largeur de l'image.
x += caracteres[c].get_width()
def rendre_image_centree(ecran, image):
centre_ecran = Vector2(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/2)
centre_image = Vector2(image.get_width()/2, image.get_height()/2)
ecran.blit(image, centre_ecran - centre_image)
def rendre_fin_de_partie(etat):
ecran = etat.rendu.ecran
if etat.simulation.collision :
#si on a perdu
rendre_image_centree(ecran, etat.rendu.texte_defaite)
elif etat.simulation.decompte_victoire == 0.0:
#si la partie est finie et qu'on a pas perdu
rendre_image_centree(ecran, etat.rendu.texte_victoire)
def afficher_rendu(etat, t):
ecran = etat.rendu.ecran
ecran.fill(COULEUR_ARRIERE_PLAN)
rendre_chronometre(etat)
rendre_fin_de_partie(etat)
rendre_projectiles(etat)
rendre_personnage(etat)
pygame.display.flip()
####################
#### NOYAU ####
####################
def chemin_vers_niveau(niveau):
repertoire = os.path.dirname(__file__)
fichier = niveau + ".lasero" #on ajoute l'extension
return os.path.join(repertoire, fichier)
def analyse_vecteur(chaine):
sans_parentheses = chaine.replace("(", "").replace(")", "")
coords = sans_parentheses.split(",")
x = float(coords[0])
y = float(coords[1])
return Vector2(x, y)
def analyse_personnage(chaine):
return Personnage(analyse_vecteur(chaine))
def analyse_decompte_victoire(chaine):
return float(chaine)
def analyse_projectile(chaine):
separee = chaine.split(" ")
position = analyse_vecteur(separee[0])
taille = float(separee[1])
direction = analyse_vecteur(separee[2])
vitesse = float(separee[3])
return Projectile(position, taille, direction, vitesse)
def charger_niveau(nom):
with open(chemin_vers_niveau(nom), "r") as f:
lignes = f.readlines()
personnage = analyse_personnage(lignes[0])
decompte_victoire = analyse_decompte_victoire(lignes[1])
projectiles = []
for l in lignes[2:]:
projectiles.append(analyse_projectile(l))
return Niveau(nom, personnage, decompte_victoire, projectiles)
def lister_niveaux():
entrees = os.listdir(os.path.dirname(__file__))
fichiers_niveaux = [f for f in entrees if ".lasero" in f]
noms_niveaux = []
for f in fichiers_niveaux:
noms_niveaux.append(f.replace(".lasero", ""))
return noms_niveaux
def charger_niveaux():
noms_niveaux = lister_niveaux()
niveaux=[]
for n in noms_niveaux:
niveaux.append(charger_niveau(n))
return niveaux
def jouer_niveau(niveau, ecran):
etat = GameState()
initialiser_controles(etat)
initialiser_simulation(etat, niveau)
initialiser_rendu(etat, ecran)
clock = pygame.time.Clock()
while not etat.controles.quitter:
if etat.controles.recommencer:
reinitialiser_controles(etat)
reinitialiser_simulation(etat, niveau)
reinitialiser_rendu(etat)
t = clock.tick(FPS)/1000
traiter_controles(etat, t)
avancer_simulation(etat,t)
afficher_rendu(etat, t)
####################
#### MENU ####
####################
class Menu:
def __init__(self, niveaux, ecran):
# Ces 4 booléens sont mis a True
# si le joueur a lancé la commande dans la frame
self.haut = False
self.bas = False
self.lancer = False
self.quitter = False
self.niveaux = niveaux
#indice du niveau sélectionné.dans la liste des niveaux
self.niveau_selectionne = 0
self.ecran = ecran
self.textes_niveaux = {}
fonte = pygame.font.SysFont("Mono", TAILLE_MENU, True, False)
for n in niveaux:
self.textes_niveaux[n.nom] = fonte.render(n.nom, True, COULEUR_MENU)
def traiter_controles_menu(menu):
## on remet à zéro les contrôles pour ne pas reprendre
## l'état de la frame précédente
menu.haut = False
menu.bas = False
menu.lancer = False
menu.quitter = False
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == TOUCHE_QUITTER:
menu.quitter = True
if e.key == TOUCHE_RECOMMENCER:
menu.lancer = True
if e.key == TOUCHE_HAUT:
menu.haut = True
if e.key == TOUCHE_BAS:
menu.bas = True
def selectionner_niveau_menu(menu):
if menu.haut:
menu.niveau_selectionne -= 1 #on remonte dans la liste
if menu.bas:
menu.niveau_selectionne += 1 #on descends dans la liste
#on vérifie qu'on ne sort pas de la liste
if menu.niveau_selectionne < 0:
menu.niveau_selectionne = 0
if menu.niveau_selectionne > len(menu.niveaux) - 1:
menu.niveau_selectionne = len(menu.niveaux) - 1
def lancer_niveau_menu(menu):
if menu.lancer:
jouer_niveau(menu.niveaux[menu.niveau_selectionne], menu.ecran)
def rendre_image_centree_decalee(ecran, image, decalage):
centre_ecran = Vector2(LARGEUR_FENETRE/2, HAUTEUR_FENETRE/2)
centre_image = Vector2(image.get_width()/2, image.get_height()/2)
ecran.blit(image, centre_ecran - centre_image + decalage)
def rendre_nom_niveau(menu, i, decalage, transparence):
ecran = menu.ecran
texte = menu.textes_niveaux[menu.niveaux[i].nom]
texte.set_alpha(transparence) #on décide de la transparence
rendre_image_centree_decalee(ecran, texte, Vector2(0, decalage))
def afficher_menu(menu):
ecran = menu.ecran
ecran.fill(COULEUR_ARRIERE_PLAN)
n = menu.niveau_selectionne
rendre_nom_niveau(menu, n, 0, 255)
# On dessine les niveaux adjacents si besoin,
# en ajoutant un décalage de position
if n > 1:
rendre_nom_niveau(menu, n - 2, -ESPACEMENT_MENU*2, 255 - GAIN_TRANSPARENCE_MENU * 2)
if n > 0:
rendre_nom_niveau(menu, n - 1, -ESPACEMENT_MENU, 255 - GAIN_TRANSPARENCE_MENU)
if n < len(menu.niveaux) - 1:
rendre_nom_niveau(menu, n + 1, ESPACEMENT_MENU, 255 - GAIN_TRANSPARENCE_MENU)
if n < len(menu.niveaux) - 2:
rendre_nom_niveau(menu, n +2, ESPACEMENT_MENU*2, 255 - GAIN_TRANSPARENCE_MENU * 2)
pygame.display.flip()
def lancer_menu(niveaux, ecran):
menu = Menu(niveaux, ecran)
clock = pygame.time.Clock()
while not menu.quitter:
clock.tick(FPS)
traiter_controles_menu(menu)
selectionner_niveau_menu(menu)
lancer_niveau_menu(menu)
afficher_menu(menu)
####################
#### MAIN ####
####################
def main():
pygame.init()
fenetre = pygame.display.set_mode([LARGEUR_FENETRE, HAUTEUR_FENETRE])
niveaux = charger_niveaux()
lancer_menu(niveaux, fenetre)
pygame.quit()
if __name__ == "__main__":
main()
Niveaux supplémentaires
Vous pouvez télécharger une archive Tar contenant plusieurs niveaux en cliquant sur ce lien.
Extrayez-en le contenu à l'aide d'un logiciel comme 7zip
, et placez les fichiers qu'elle contient à côté du script du jeu.
En lançant le jeu, vous verrez les différents niveaux dans le menu. Attention, certains sont très difficiles !