
Miaouno
2025 - 2026- Python
- Pygame
- Projet IUT
Le Miaouno est un jeu de Uno en multijoueur réseau avec une interface graphique créée à l'aide de Pygame
Avant tout, un aperçu du projet
Contexte
Pendant ma 2e année de BUT informatique, dans le cadre du module de "développement efficace", nous avons dû réalisé, par binôme, un projet qui implémentait des structures de données comme des listes chaînées, des piles, des files, ou encore des arbres. Nous avons donc porté notre choix sur un jeu de Uno, revisité avec le thème des chats. Pour rendre le projet un peu plus intéressant, nous avons également décidé de rendre le jeu multijoueur en LAN, et nous avons implémenter une interface graphique avancée. Ce projet a donc été développé en Python, avec l'aide du package Pygame pour créer l'interface graphique.
Architecture du projet
La gestion du contexte du jeu et de ses états
La classe GameContext représente le contexte global du jeu qui doit être accessible dans tous les états du jeu. Il contient :
- Un GameStateManager
- Une GameSession
Le GameStateManager
La classe GameStateManager est une classe qui contient une pile de GameState. Les GameState sont les états principaux du jeu, par exemple: Le menu principal, le menu pour trouver une partie, le menu où le joueur joue au Uno, etc. Ils implémentent les méthodes update et render.
La GameSession
La classe GameSession est la classe qui a pour responsabilité de gérer la session de jeu. Elle fait donc le lien entre le Uno / Les traitements réseau (Host ou Client) / et l'affichage. Selon les choix de l'utilisateur dans l'UI, le GameContext va soit créer une HostSession, soit une ClientSession (qui vont ensuite créé respectivement un Host ou un Client).
La boucle de jeu principale
La boucle de jeu principale qui se trouve dans le main, effectue principalement les actions suivantes :
- Sélectionne le GameState au sommet du GameStateManager
- Mise à jour de celui-ci
- Affichage de celui-ci
Exemple concret
Lors de la création du jeu, on déclare le GameContext qui se charge de créer le GameStateManager. On empile dans le GameStateManager le menu principal. La boucle du jeu se lance, elle met à jour l'élément au sommet du GameStateManager, soit, le menu principal (MainMenu) (Image 1). Le menu principal, qui est un GameState, possède donc une référence vers le GameContext pour lui-même changer l'état du jeu. Lorsqu'on clique sur le bouton “Créer une partie”, le menu principal empile au sommet du GameStateManager le menu CreateGameMenu et celà a donc pour résultat de voir s'afficher le CreateGameMenu car c'est lui le nouveau sommet de la pile (Image 2). Pour revenir en arrière, lorsqu'on clique sur le bouton “Retour”, le CreateGameMenu se dépile lui-même du GameStateManager ce qui a pour résultat d'afficher à nouveau le MainMenu. (Image 3)
La gestion du réseau
Avant tout, il est important de comprendre quels choix nous avons fait sur le fonctionnement du jeu en réseau. Nous avons décidé qu'un joueur ait 2 possibilités lorsqu'il lance le jeu : créer une partie, ou en rejoindre une. S'il en crée une, il va être un host, et s'il souhaite en rejoindre une, il va être client. Le client peut détecter les hosts sur le même LAN que lui afin de trouver une partie
La classe Host
La classe Host a plusieurs objectifs :
- Faire savoir aux clients qui recherchent des parties sur le même réseau local que celui-ci existe. Pour celà, il envoie régulièrement en broadcast UDP l'information qu'il est disponible
- Connecter les clients (TCP) qui le demande (si c'est possible)
- Gérer les clients une fois qu'ils sont connectés, recevoir leurs messages, leur en envoyer (via TCP)
La classe Client
La classe Client a plusieurs objectifs :
- Rechercher des hosts disponibles s'il n'est pas déjà connecté. (Il reçoit les broadcast UDP des hosts pour savoir que ceux-ci existent)
- Se connecter à un host (TCP)
- Gérer les messages reçus de l'host et en envoyer
Les classes HostSession / ClientSession
Comme expliqué précédemment, la classe GameSession est la classe qui a pour responsabilité de gérer la session de jeu. Elle fait donc le lien entre le Uno / Les traitements réseau (Host ou Client) / et l'affichage. Selon les choix de l'utilisateur dans l'UI, le GameContext va soit créer une HostSession, soit une ClientSession (qui vont ensuite créé respectivement un Host ou un Client). Ce sera la HostSession qui va se charger de gérer le déroulement du jeu. En effet, c'est elle qui va être le coeur de la partie. Elle gérera le Uno, vérifiera les demandes des clients reçues via la classe Host, leur répondra, etc. Que ce soit ClientSession ou HostSession, suite à des actions de la partie, elles notifient l'UI des joueurs pour leur permettre de voir ce qu'il se passe.
La classe Uno
C'est elle qui gère le coeur du jeu / la logique métier. Elle est indépendante du réseau et des autres classes. C'est le HostSession qui l'utilise afin que la partie se déroule.
L'interface graphique
L'interface graphique utilise la librairie Pygame pour fonctionner. Nous avons décidé d'implémenter une surcouche qui nous permet d'avoir des composants UI réutilisables. Pour se faire nous avons implémenter une classe UIComponent qui sert de base pour tous les autres composants UI. La classe peut posséder de nombreuses propriétés telles que des bordures, fonds, couleur, image, taille, position, etc. A partir de cet UIComponent nous en avons créé d'autres comme UIText, UIButton, UICard. Nous avons également créé un UITextInput. Afin de gagner du temps, nous avons fait appel à une petite librairie qui s'appelle pygame-textinput, elle permet simplement de récupérer les inputs et les afficher via une interface pygame. Afin de permettre la mise à jour de l'UI de façon dynamique, nous avons utilisé le design pattern Observer.
Autres
Afin de gérer correctement le fonctionnement du projet, nous avons parfois utilisé des threads par exemple dans le Client et l'Host. L'objectif est de faire tourner parallèlement à l'interface utilisateur la gestion du réseau pour que l'interface ne fige pas toutes les 2 secondes. Nous avons essayé de sécuriser au maximum les threads pour éviter les problèmes notamment grâce à des sémaphores. Pygame qui n'est pas thread-safe à rendu la tâche plus compliquée, la mise à jour de l'UI étant difficile à sécuriser puisque les données viennent d'un thread réseau.


