EDIT : bonne lecture !
Je me permets d'écrire un autre message à la suite du mien
J'ai un peu regardé ton fichier jeu.py.
D'abord, je tombe sur ta série de listes/dictionnaires qui te servent de constantes. C'est pas propre du tout de les déclarer dans la fonction qui exécute le jeu. Et leur sémantique est assez obscure (on ne sait pas qu'est-ce qui représente quoi), je préfèrerais utiliser des classes qui gèrent tout ça.
Et surtout ta fonction jeu_boubouille est énoooooooooooorme
Je vois que tu déclares des fonctions dedans. Dans ce cas, autant créer une classe Jeu qui contient la méthode sauvegarder, par exemple, et heu… c'est quoi ces noms de fonctions ? J'ai pas le temps d'essayer de comprendre tout le code, tes noms de fonctions doivent être explicites (= pas besoin de regarder le code pour comprendre ce qu'elles font). De plus, j'ai l'impression que certaines sont inutiles et devraient être regroupées en une seule, ou d'autres qui en font trop. Et elles ont presque toutes trop de paramètres.
Ta boucle principale de gestion des évènements est extrêmement trop énorme. Il y a un gros travail à faire là-dessus. Tu ne dois surtout pas avoir de logique dans la gestion d'un évènement. Quand tel ou tel évènement est vérifié, il délègue le travail : c'est pas le sien de déplacer le joueur, mettre à jour le bloc actuel etc.... Parce que là, tu répète des morceaux de code dans presque tout tes évènements et tu as une sémantique complètement mélangé.
J'ai mis un peu de temps avant de trouver à quoi correspondait le paramètre s
envoyé un peu partout. Et au final, j'ai pas compris ce que contenait _S_ (EDIT: ah oui c'est vrai, le zmarkdown interprète les '_') (je sais juste que tu le charge de "s.sav"). Ton code est assez mystérieux.
Ce n'est qu'un aperçu de toutes les critiques que je pourrais faire, y a énormément de travail à faire !
Arrivé là, le plus efficace est de TOUT RÉÉCRIRE de zéro (et en profiter pour faire de l'objet, comme tu as commencé à le faire ). Certains morceaux de codes seront à reprendre, peut-être.
Oui, je sais, ça va te prendre beaucoup de temps. Mais après, ce sera bien plus simple d'ajouter quelque chose et surtout de pouvoir t'aider. Là c'est vraiment le gros bazar.
Et c'est pas grave si tu n'ajoutes pas de fonctionnalité avant 1 mois ! C'est très important de nettoyer un code dès qu'on a terminé d'en ajouter une. Quand je dis nettoyer, c'est prendre du recul, regarder ce qui ne va pas. Et si besoin, ré-organiser certaines choses (parfois tout, comme c'est le cas ici, mais ça ne devrais pas arriver si tu as bien structuré dès le départ), enlever des choses inutiles, abandonner une fonctionnalité ingérable (c'est plutôt rare), factoriser des morceaux de codes, encapsuler des données dans une classe etc…
Pour te mettre sur une bonne voie et te donner une idée, je vais te montrer ma façon de fonctionner.
D'abord, je commence par écrire la logique globale du jeu dans du pseudo-code :
| def run_game :
initialiser jeu
tant que le joueur ne quitte pas :
délai de 20 ms # ou faire un calcul en fonction des FPS
gestion évènements
affichage
quitter jeu
|
Normalement je détaille bien plus, mais je préfère résumer, mon message prend déjà beaucoup de place.
Tout de suite, je me dis "tiens, une classe Jeu qui se charge d'initialiser, afficher et quitter le jeu, ça serait pratique". Et ben c'est parti, j'imagine qu'elle existe déjà et je m'en sert (et je fais de même pour toute fonction/classe qui me semble utile) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 | def run_game():
jeu = Jeu(RESOLUTION) # Constante définie dans constantes.py
joueur = jeu.creer_joueur() # Le jeu gardera une référence du joueur.
fin = false
while not fin:
# Sur cette ligne, un délai de X ms en fonction des FPS.
# Puis la gestion des évènements.
for e in pygame.event.get():
if event.type == QUIT:
fin = true
if event.type == JOYAXISMOTION:
if event.axis == 0 and event.value > 0:
joueur.deplacer_a_droite()
if event.axis == 0 and event.value < 0:
joueur.deplacer_a_gauche()
if event.axis == 1 and event.value > 0:
joueur.accroupir()
if event.axis == 1 and event.value < 0:
joueur.sauter()
if event.type == JOYBUTTONDOWN:
if event.button == 0:
joueur.taper()
if event.button == 1:
joueur.poser_bloc()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
fin = jeu.menu() # Vu que le menu entrera dans sa propre boucle de jeu, ça mettra automatiquement le jeu en pause. Renvoie si le joueur décide de quitter.
# Mettra à jour automatiquement le joueur via un joueur.mettre_a_jour() grâce à la référence interne dans jeu.
jeu.mettre_a_jour() # Ou jeu.update() si tu préfères ^^
jeu.afficher()
jeu.quitter()
|
Si tu regardes le code, pas besoin de voir ce qu'il y a dans les classes Jeu et Joueur pour savoir ce qu'il se passe.
D'ailleurs, il peut y avoir un peu ce qu'on veut comme code à l'intérieur. Ce qui montre que si on souhaite changer une logique du jeu, voir le changer radicalement, les modifications se feront discrètes, faciles et proprement (ça demandera quand même du travail, bien sûr ).
Ça donne une bonne idée d'un jeu structuré, où chaque objet/méthode/fonction ne fait pas plus qu'une chose à la fois. run_game fait un peu exception : elle charge le jeu, exécute la boucle principal et quitte le jeu (d'ailleurs, on peut facilement le "résoudre"). Mais chaque méthode de joueur ne fait bien qu'une seule chose à la fois, ainsi que les méthodes de l'objet jeu.
Chaque méthode mettre_a_jour()
dans objet.mettre_a_jour()
met à jour les attributs de l'objet (et rien d'autres) en fonction son état et de son environnement. Ensuite, jeu.afficher()
ne fait QUE afficher (à-priori des séries de objet.afficher()
, comme carte.afficher(), joueur.afficher(), barre_inventaire.afficher() etc…). joueur.sauter()
ou joueur.accroupir()
ne font que modifier l'état du joueur mais ne modifient pas les coordonnées (mais peut-être la vitesse) de l'objet. Elles n'affichent rien non plus. Ces objets auront logiquement chacun leurs coordonnées et éventuellement vitesses.
Maintenant, on peut définir les fonctions/classes que j'ai utilisé qui n'existe pas encore. Et j'utilise la même logique : plutôt que d'écrire tout le code du menu dans jeu.menu()
, j'invente des fonctions/méthodes/classes qui me permettront d'avoir un code clair, propre et structuré (comme une classe Bouton, une classe Options, une méthode jeu.sauvegarder()
, une méthode jeu.charger_options(nouvelles_options)
par exemple). Et je fais ça à chaque fois, jusqu'à définir le code "bas niveau". Concrètement, on arrivera aux pygame.display.blit()
, self.x += self.vx
et self.objet_actuel = objet_selectionne
dans la classe Joueur par exemple.
En gros, je définie le cahier des charges des fonctions/classes en écrivant la logique générale d'abord, puis en allant de plus en plus vers le bas niveau du code. Pour ce genre de jeu où l'organisation n'est pas très difficile à faire, ça marche très bien (enfin, avec moi en tout cas ).
Mais pour certains logiciels à développer, c'est loin d'être aussi simple. Parfois, il faut réfléchir pendant plusieurs heures avec un crayon et du papier pour savoir comment résoudre un problème et garder un code évolutif et modulable. Dans ces cas-là, il existe probablement un design pattern (patron de conception) qui répond au problème.