Skip to content

8area8/mysterious-lab

Repository files navigation

<title>README</title> <script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"></script>

Mysterious labyrinth

Table des matières


enter image description here


Bonjour chers utilisateurs!

L’exercice donné dans le cours était déjà compliqué… Mais allez savoir pourquoi, je me suis mis des barrages en plus, en voulant en faire une application fenêtré.

L’idée de base était de rendre le programme facilement diffusable; je me suis penché sur tkinter, module de base de python.
Et… ce module m’a fait tirer les cheveux plus d’une fois, la gestion de l’espace étant pauvre à l’extrême!

J’ai donc finis par me pencher sur pygame, et après plus d’un mois de codage, j’arrive enfin à un résultat exploitable..!

Note: Je reconnais que j’ai fait beaucoup plus que ce qui était demandé, et que le projet peut être incompréhensible, car il utilise des outils qui n’ont pas été abordés par le tuto. Mais j’ai voulu me faire plaisir dans cette exercice, et vous faire partager une version qui sort de l’ordinaire (et qui peut être vous donnera envie de vous plonger dans pygame).


Respect des consignes

Je vais rapidement vous passer les spécificités du jeu, en rapport avec les consignes données par l’exercice. L’application étant fenêtré, certaines choses ont dus être faites sous un angle différent.

Control du robot

Les contrôles de base étaient simples; avancer à gauche, à droite, haut et bas une ou plusieurs fois. J’ai pour le coup un peu dévié de la demande, en intégrant un système de pathfinding plus recherché.

Si vous connaissez Dofus et Wakfu, le système est le même. Le personnage possède des points de mouvements, qui détermine son déplacement maximal.
Il peut ensuite se déplacer dans un rayon de cases tout autour de lui, en empruntant un chemin déterminé:

  • par la souris (en faisant glisser la souris de la case du héros jusqu’à la case voulue)
  • par un système de recherche automatique, qui va déterminer les cases possibles, et le chemin à afficher si la souris pointe sur une case possible.

Note: L’alliance des deux est d’ailleurs possible; après avoir déterminé un chemin avec la recherche automatique, on peut modifier ce chemin en glissant de case en case avec la souris.

enter image description here

  • Le jeu trouve un chemin depuis le curseur de la souris, et par glissement de souris, on enlève ou ajoute une case…

Affichage du labyrinthe

J’ai repris les cartes fournies dans l’énoncé; et j’ai crée un affichage vue de dessus en associant chaque symbole à des images. Tout est respecté, c’est assez classique.

Note Un menu est aussi présent, pour effectuer certaines actions (la plupart des boutons présents dans le menus ne sont pas encore implémentés).

Fonctionnalités du jeu

Le jeu enregistre automatiquement la partie et ce en permanence (oui je sais, c’est pas très opti!).
J’ai d’ailleurs fait quelques modification pour améliorer les performances, il n’enregistre plus que… 30 fois par secondes (c’est énorme je sais)… Contre un nombre bien plus important avant. Point à modérer malgré tout: les données à enregistrer sont minimes (le personnage, et le nom de la map).

Les cartes sont au nombre de deux, ce sont celles fournies par l’exercice. Je ne les ais pas modifiés, et vous pouvez en rajouter autant que vous voulez.
Attention par contre, deux points à prendre en compte:

  • J’ai pas fini d’implémenter le menu de sélection de niveaux. Ce menu montre les cinq premières cartes, et doit ensuite permettre via des flèches un défilement vers la droite ou la gauche, comme si on lisait un livre. Et bah, ces flèches n’existent pas encore. :) Vous n’aurez donc que les 5 premières cartes de dispo.
  • Si vous créer une carte, il existe un calcul que je n’ai pas revu encore, lors de la génération de la carte. Il spécifie la largeur de la map selon la première ligne de symboles. Donc faites en sorte que votre première ligne soit aussi large ou plus large que les autres (sinon votre map sera coupé).
  • Ah quand même, dernier point: je vous conseille dans tout les cas, de faire une map au moins aussi large et longue que la carte ‘facile’ donné par l’exercice, et de ne pas trop tenter de différer la largeur entre chaque ligne: je ne sais pas si mon module caméra appréciera (encore du code à revoir)! :D

Au lancement du programme

On nous propose de commencer une partie, d’en charger une ou de quitter le jeu.
Le bouton charger une partie se grise s’il n’y a aucune partie à charger (obvious).
C’est ma façon de proposer de rejouer une partie, sans l’utilisation de texte, juste par l’aspect visuel.

enter image description here

Les conditions de notation

  • Le programme fonctionne en le lançant. Il y a cependant le principale problème de ma version, que j’explique à la fin de ce document dans la partie l’instant qui fâche.
  • le code est lisible; j’ai dus réécrire tout le système une seconde fois en délimitant bien chaque partie dans différents fichiers. Les conventions de nommages python sont respectés. Cependant vous remarquerez que je nomme mes classes/variables en anglais. Ecrire en anglais est une bonne habitude à prendre en programmation, même pour moi qui n’aime pas cette langue.
  • Le projet est découpé en plein de fichiers différents. Certains ne contiennent presque rien, mais permettent une évolutivité importante. Seul petit point: j’aurai peut être dus départager la partie qui met à jour les éléments, de la partie qui dessine les éléments. Et dans la même veine, certains événements ne sont en fait que des mises à jours (genre de chose qu’on se rend compte avec le temps).
  • Le projet est souvent documenté (et cette fois ci, en français); j’essai d’être le plus concis possible (tout en étant claire): trop de documentation nuit à la lisibilité du code.
  • pour ce qui est de l’ouverture à l’amélioration: j’ai découpé mon projet en pleins de parties différentes, que j’ai essayé au maximum de rendre indépendantes. Cela m’a permit d’enlever et rajouter facilement des éléments, et pour la suite ça me permettra de modifier le projet à mon aise.
  • Petit point de fin; j’ai voulu faire quelque chose des portes, elle ‘s’allument’ et font un son quand le personnage passe dessus.

Explications sur Pygame

Alors, pygame est un gros module qui crée des applications fenêtrés, avec gestion des images, du son, de l’animation, des événements…Il est parfait pour faire des petits jeux. Ma connaissance en la matière n’est clairement pas exhaustive, j’ai appris sur le tas et je continus d’apprendre, je peux cependant vous donner une vision d’ensemble, et mieux vous faire comprendre mon petit jeu.

Comme la plupart des modules d’applications fenêtré, pygame repose sur une boucle qui tourne en permanence lorsque le programme est ouvert.
Dans cette boucle, on aura dans l’ordre:

  • L’appel des différents événements (appuie sur une touche, clic de souris…) et des méthodes qui les utilisent.
  • La mise à jours des différents éléments du jeu (on change leur positions sur les images).
  • Le dessin des différents éléments du jeu(on ‘colle’ les images à l’écran).
  • Puis un rafraîchissement global du jeu (méthode pygame.display.flip()).

A cela s’ajoute une variable de temps, qui va contrôler le nombre d’images par seconde (FPS).

Les surfaces

pygame utilise des ‘surfaces’, qui sont des sortes de conteneurs d’images, sur lesquels on colle un peu tout ce qu’on veut. Chaque surface sera collé en définitive à l’écran principal, celui qui affiche tout (qui est définit par pygame.display.set_mode(size)).

Created with Raphaël 2.1.2petite surfacegrande surfaceécran principal

Dans mon projet, j’ai un module ‘surface’ qui comprend pratiquement toutes les images de mon jeu, pour les différents moments du jeu (menu et ingame).
Je pioche ensuite ces images (qui sont de petites surfaces) à l’appel des classes correspondantes, pour les coller à des surfaces plus grandes.

Les Sprites

Chacune des images crée utilise d’autres modules crées par mes soins, qui sont pygame_extend, et pygame_xtd_entity, pour le personnage principale.

Les classes de ce module héritent de la classe Sprite de pygame, une classe qui gère les différentes images et qui permet de les regrouper facilement dans des ‘groupes’ conçus pour, de les mettre à jour, et les dessiner à l’écran.

note: on utilise donc une variable de type pygame.sprite.Group(). Cette variable
met à jour tout les sprites contenus avec la méthode update(), et les dessine avec la méthode draw().

pygame_extend et pygame_xtd_entity sont un aspect technique du programme et assez bas niveau (on doit définir beaucoup de choses pour un résultat simple), c’est donc certainement chiant à lire. :)
Ils me permettent la création d’images simples, de boutons, d’images animés…

Les événements

Ils sont appelés lors de la boucle principale du jeu. J’utilise ensuite une méthode events() dans mes classes de menus et du jeu, qui va prendre l’événements courant et le tester en fonction des méthodes implémentés.

Un module events.py a aussi été crée; il est instancié dans mes classes de menus et de jeu (qu’elles appelleront ensuite via leur méthode event). Il permet une meilleure répartition du code.

Les musiques et le temps

C’est très peu documenté car j’en fais une application très simple (encore plus pour le temps), et qu’ils restent assez évidents à lire.
J’ai crée un module musics.py par soucis d’organisation, instancié aussi lors de l’appel des classes principales.


Agencement du jeu

Bon, là on rentre dans le gros du travail.

Concrètement, je démarre par le fichier roboc.py (comme demandé), qui instancie pygame et la boucle du jeu, dans le fichier loop.py

La classe Loop va instancier une variable, self.active_screen, qui contiendra les différentes classes de menus et du jeu. Lors de l’instanciation, la variable appel directement le menus d’écran titre.

Created with Raphaël 2.1.2active_screen: valeurs possiblesactive_screenactive_screenTitleMenu()TitleMenu()SelectLevel()SelectLevel()Game()Game()module menus.pymodule menus.pymodule game.py

Ensuite vient la boucle. Cette boucle teste les événements de la variable self.active_screen, et met à jour self.active_screen en permanence.

Voici ce qu’il se passe lorsque la variable self.active_screen prend la classe Game en valeur:

Created with Raphaël 2.1.2Appel des modulesactive_screenactive_screenGameGameMusics.Game()Musics.Game()Surfaces.Game()Surfaces.Game()Events.Game()Events.Game()appel de Game

Comme j’ai compartimenté la plupart des éléments dans des fichiers séparés, Game va aller chercher ces fichiers un à un.

Mais Game, contrairement aux autres classes principales qui gèrent les menus, va instancier en plus d’autres modules, complexes (cartography.py, camera.py et pathfinder.py), et moins complexes (entity.py, case_entity.py).

Nous allons d’ailleurs voir les modules particuliers au jeu.

Le module data

Ce premier module est relativement simple; il permet d’importer les noms de fichiers de cartes pour le menu de sélection de niveau, et de gérer les sauvegardes de données lors du lancement du jeu.

Il crée aussi un liste des symboles contenus dans la carte choisie. Cette liste sera utilisé par la module cartography.py

Le module cartography

Appelé dans la classe Game, la classe Map de ce module se charge de créer la carte du jeu (et le héro par la même occasion).

Elle a besoin pour cela, du nom de la carte choisie. Avec ce nom, elle appel la méthode du module data.py, qui renvoi une liste contenant tout les symboles de la carte choisie.
Après, la classe va créer un dictionnaire avec comme clés, les coordonnées, et comme valeurs, les objets correspondant aux symboles associés.

exemple:

  • coordonnées[0, 0] = mur
  • coordonnées[1, 1] = chemin

Ces objets sont définis dans le module case_entity.py, et contiennent simplement une variable d’image (leur apparence dans le jeu), leur coordonnée, et un booléen solid, qui permet de tester si le personnage peut passer dessus ou non.

note: le héro est crée dans cette classe, de la même manière que les autres objets (avec le module entity.py).

Le module camera

Ce module, appelé dans la classe Game après le module cartography, définit une surface qui va englober la carte du jeu, et la déplacer selon diverses méthodes.

Le principe, simple, consiste à afficher la carte à différentes positions de l’écran (qui est représenté par la surface caméra).

La caméra permet de bouger la carte via un clic droit de la souris enfoncé (méthode définit dans events.Game), et de suivre le personnage, quand ce dernier bouge.

note: La caméra évite aussi de pouvoir sortir de la carte

le module pathfinder

Celui qui m’a prit le plus la tête.

Basiquement, et comme expliqué plus tôt, la classe PathController est appelé dans les événements de Game, et fait un calcul en fonction de la position de la souris.

Ce calcul détermine si un chemin est possible depuis le héro jusqu’à la souris.

La classe DisplayPath se contente d’afficher le chemin, s’il y en a un.

Enfin, Game possède un événements qui se charge de faire bouger le héro si on clique sur un chemin.

note: changer la valeur de la variable moves_point de la classe Hero (présente dans entity.py) changera les possibilités de déplacement (plus de case ou moins de cases possibles)

le reste

J’ai développé chacun des fichiers présents dans le dossier du jeu, et vous devriez avoir une vision global du code maintenant.

Je ne détail pas la classe du personnage, car je la trouve simple; seul le mouvement serait intéressant à expliquer, mais il faut juste retenir que dans un jeu, chaque mouvement se fait en parallèle d’un autre; et pour y parvenir, on doit faire bouger chaque chose une à une petit à petit, frame par frame, image par image.


Contenu du jeu

Je ne vais pas m’attarder plus, mais une fois dans le jeu:

  • Vous pouvez déplacer la caméra avec un clic gauche enfoncé de la souris.
  • vous avez une croix en haut à droite de l’écran, qui affichera un petit écran qui vous proposera de revenir au menu principal.
  • vous pouvez cliquer sur une case ‘verte’ (un chemin), pour faire bouger le héro jusqu’à cette case.
  • enfin, si vous atteignez la carte, vous aurez une animation de victoire. Cliquez n’importe où pour revenir à l’écran titre.

par contre:

  • le menu du bas est purement décoratif à l’heure actuelle. Vous avez cependant les coordonnées de la souris qui s’affichent en bas à gauche en continue.

L’instant qui fâche

Et voici venu l’instant qui fâche! ‘cris et sifflements

Utilisant une bibliothèque qui n’est pas native à python, j’ai dus la télécharger sur le site officiel. Je pensais me dépatouiller avec cx_freeze, qui transforme l’application en un executable. Seulement deux choses:

  1. j’arrive pas à l’installer (‘ouh le nul!’).
  2. et ça m’a pas plus donné envie de résoudre le premier problème, quand j’ai su qu’il ne compilais pas directement le programme sur toutes les plateformes, mais seulement sur la plateforme actuelle (donc amis MAC et LINUX, pas de chance pour vous…!)

Alors je me suis simplement dis que vous l’installerez, puisqu’en tant qu’apprentis programmeurs comme moi, vous devriez y arriver dans trop de problèmes.

Voici le lien qui vous permettra d’installer pygame:
http://www.pygame.org/wiki/GettingStarted#Pygame

J’ai programmé le jeu en python 3 (version 3.2 pour être exact, mais ça ne devrait pas du tout importer). Faites juste attention, si vous avez plusieurs versions de python (l’enfer pythonien), d’appeler celle qui contient la librairie pygame (peut être aurez vous aucun problèmes, contrairement à moi). :)

Cool Story bro’, mais si je veux pas l’installer?

Ah, là j’oblige personne. Je vous ai donné du travail supplémentaire pour un exercice normalement simple à corriger, et je m’en excuse…
Si vous ne voulez pas vous embêter à télécharger pygame, corrigez sans tester et mettez la note que vous voulez.


Le mot de la fin

Si vous êtes arrivés jusqu’ici sans sauter de parties, je vous félicite! Un pavé pareil pour une simple correction, c’était pas donné d’avance.

Merci d’avance si vous prenez le temps de télécharger pygame, et merci dans tout les cas pour la correction. :)

-Mikael B.

Written with StackEdit.

Releases

No releases published

Packages

No packages published

Languages