Projet SnZ



Hosted on GitHub Pages — Theme by mattgraham

Introduction

Le but de ce projet est de réaliser une application client/serveur dans un contexte MMO. Notre jeu est un jeu de survie à la première personne. Dans un monde postapocalyptique, le joueur doit survivre dans un environnement hostile peuplé de zombie.

Ce projet se divise en deux parties, un serveur et un client. Chaque client peut se connecter au serveur. Le client est implémenté en C# avec Unity tandis que le serveur est implémenté en C++. La connexion entre le client et le serveur se fait uniquement en TCP.

Ce projet a été réalisé par :

  • Antoine Martin aka hyptos (Connexion Client/Serveur)
  • Antoine Richard aka Avzgui (IA)
  • Clément Picq aka IroneP (Connexion Client/Serveur)
  • Adrien Faure aka oldDadou (Serveur)
  • Cristophe Pinelli aka Kappasaleth (IHM)
  • Raphael Fiume aka McCowboy (Unity)
  • Cynthia Perret (IHM)
  • Lucas Bercheti aka Dragzal (Unity)

Vous pouvez accéder au repository de la partie serveur en suivant ce lien :

Vous pouvez accéder au repository de la partie client en suivant ce lient :

Organisation et conception

Répartition des tâches

La semaine dédiée à cette UE nous a permis de travailler et de prendre place au sein du groupe. Avant de commencer à coder. Nous avons pris soin de faire en sorte de tirer meilleur parti des compétences de chacun. Ainsi nous nous sommes chacun attribué un "rôle" dans l'équipe et le projet.

Conception

Afin d'être efficaces et autonomes nous avons, avant de commencer, procédé à une phase de conception communes. C'est à ce moment que les choix importants ont été fait.

  • Le concept de notre jeu
  • Le modèle IA
  • Les besoins réseau et échanges de méssages
  • L'interface utilisateur de notre jeu
Ces échanges ont été utiles autant sur le plan conceptuel (gameplay, gamedesign etc ), que sur le plan technique. La participation globale des membres du groupe nous a permis, par la suite, d'être plus efficaces. Tout les membres du groupe avait après la phase de conception une vision globale du projet et un rôle a jouer.

Partie Serveur

avant propos

La partie serveur a été commencée depuis le début de la semaine intensive, car un membre était motivé pour créer cette partie du projet. Or le serveur de base, fourni avec le projet, ne présentait pas de fonctionnalité multi threadée, et donc aucune gestion de plusieurs clients (ce qui est dommage pour un jeu multi joueurs). La première partie de l'implémentation du serveur de communication consistait donc à définir et créer cette implémentation afin de permettre la connexion simultanée de plusieurs joueurs. Au milieu de la semaine intensive, une version multi threadée proposant la gestion de plusieurs clients fut ajoutée au projet principal. Nous avons donc abandonné celui que nous concevions, pour utiliser celui finalement fourni.

Présentation

Le serveur de communication est implémenté directement par-dessus le serveur de socket fourni avec le projet. Il présente une interface de passage de messages et permet la connexion simultanée de plusieurs clients. Même si destinée à notre jeu, celui-ci est tout à fait générique.

Les messages

Chaque donnée devant être envoyée/reçue par le serveur devra implémenter une classe virtuelle pure de message générique (IMessage). L'interface IMessage demande l'implémentation des fonctions permettant l'encodage de la donnée en flux d'octets, ainsi que de spécifier le tag du message. Ceci servira à l'identification du type de message, et est nécessaire pour l'identification des flux d'octets.

Les interfaces du serveur

Ensuite, le serveur de communication présente deux interfaces, la première permet l'envoi de messages. En identifiant un client grâce à un id, le serveur est capable d'envoyer le message. La seconde interface permet la réception de messages. Chaque module présent sur le serveur désireux de recevoir un type de message devra alors implémenter une interface ("MessageHandler"). Il suffira ensuite de s'abonner au serveur en notifiant le type des messages souhaité. Il est à noter qu'il est de la responsabilité du MessageHandler de décoder le message, car le serveur transmettra uniquement un flux d'octets ainsi que le type de message transmis (ce qui permet de s'abonner à plusieurs types de messages).

Une fois le travail du serveur fini, il suffit de définir les messages que nous utiliserons (même à la volée si nécessaire) et de fournir les fonctions pour les transformations binaires/structures.

Diagramme de classe illustrant la machinerie interne du server.

Les échanges réseau

Dans ce paragraphe, nous parlerons des échanges réseau. Nous utiliserons le mot "trame" à la place de "message". Les messages sont un procédé "haut niveau" de notre application. Afin de pouvoir dialoguer grâce au socket, nous avions besoin d'un format générique de trames. En effet, le nombre d'informations que nous devons faire transiter entre le serveur et le client est indéfini (assets, mouvement/action joueur etc). Nous avons donc gardé l'implémentation faite avec les byteBuffers (envoie de la taille, puis lecture de la trame), et nous avons ajouté un caractère au début de la trame afin de différencier les différentes trames réseau.

taille : int id trame : char Message : bytes
Format d'une trame

Partie Client

Le principal rôle de la partie client est de faire le lien entre ce que nous envoie le serveur et ce dont Unity a besoin pour fonctionner. Pour cela, nous avons créé plusieurs fonctions qui récupèrent une chaine d'octects, et selon l'ID qui a été envoyé en tête de la chaîne, récupère le message voulu. Ainsi, en imposant un formalisme précis entre le client et le serveur, on peut faire transiter des messages de type Mesh, String, Event (type que nous avons créé pour signaler un mouvement) et connexion.

De plus, il est de la responsabilité du client d'informer en temps réel la position du joueur au serveur. Le serveur, lui, va envoyer continuellement la position de chacun des zombies pour que le client puisse mettre à jour son affichage. Tous les mouvements des zombies sont calculés par le serveur.

Nous avons deux protagonistes :

  • Zombie ( contrôleur a la troisième personne avec animation )
  • Joueur ( contrôleur a la première personne sans animation)
  • Joueur ( vue a la troisième personne afin d'intensifier l’immersion) déplacement (zqsd) avec mouvement de la tête par la souris
  • Passage du menu au jeu par un système de chargement afin que la map soit chargée avant l'entrée en jeu
  • Bande-son ajoutée

Partie Intelligence Artificielle

Le but de notre projet était de créer un Système Multi-Agents (SMA) représentant un écosystème post-apocalyptique. Nous avons donc voulu que des zombies parcourent l'environnement, se déplacent et réagissent de manière "vraisemblable" et indépendante du joueur, c'est-à-dire qu'ils ne soient pas simplement des bots ayant une zone de déplacement et réapparaissant après un certain temps.

Nous voulions que nos zombies se déplacent en "horde" à travers l'environnement, qu'ils réagissent aux sons, à la lumière et à la vue d'un ou plusieurs joueurs.


Afin d'implémenter ce SMA nous avons utilisé une implémentation permettant de lancer les agents à l'aide de threads en suivant le schéma suivant :

  • Le modèle créé un Environnement et des Entités, entités qu'il lie à l'environnement lors de leur création.
  • Chaque entité possède un corps dans l'environnement.
  • Un agent est une entité, mais une entité peut être un objet, un joueur ou un agent.
  • Le corps de chaque agent possède un certain nombre de modules "senseurs" captant les divers stimuli envoyé depuis l'environnement.
  • Le corps de chaque agent possède un certain nombre de modules "moteurs" lui permettant d'effectuer des actions sur l'environnement.
  • Les Stimuli peuvent être sonores ou visuels et sont générés depuis l'environnement.
  • Chaque module est lancé dans un thread.

Pour donner aux zombies le comportement que nous souhaitions, c'est-à-dire :

  • Se déplacer en groupe.
  • Être attiré par le bruit.
  • Attaquer les joueurs.
Nous avons implémenté un algorithme de flocking, afin qu'ils se regroupent et se suivent de manière plus ou moins "organisé", en y ajoutant de légères modifications pour qu'ils soient attirés par les joueurs (au bruit ou à la vue) et qu'ils soient légèrement moins "organisés" lorsqu'ils se dirigent sur un joueur étant entré dans leur champ de vision.

Ainsi nous obtenons le comportement souhaité : des hordes de zombies parcourant l'environnement et pouvant tomber à tout moment sur le joueur.

Ce qui fonctionne

Voici les fonctionnalités implémentées :

  • Connexion client/serveur
  • Envoi de mesh au client
  • Envoi/Reception de données au client
  • Envoi/Reception de données au serveur
  • Système multiagent côté serveur

Les améliorations possibles

  • Ajouter les zombies dans le client de manière dynamique.
  • Mettre en place un système de "chunk" envoyé depuis le serveur.
  • Utiliser du TCP pour la connexion/déconnexion du joueur, puis de l'UDP pour l'envoi des positions des entités.
  • Refonte du Moteur IA afin de pouvoir lancer les agents dans des processus plutôt que dans des threads et utiliser des temps logiques plutôt que des temps réels.
  • Ajout de divers agents :
    • Des chiens, pouvant s'attacher ou non aux joueurs, les aider, trouver des objets. Pouvant se déplacer en meute, communiquant par "aboiements".
    • Divers animaux pouvant être "élevés" par les joueurs : vache, poulet, cochon ... Mais pouvant aussi être attaqués par les zombies.
    • Des arbres, plantes et céréales, pouvant être cultivés par les joueurs et réagissant au soleil et à l'eau pour croitre.
  • Créer des interactions entre joueurs, agents et environnement.
  • Créer un système de "craft".

Screenshots

Attention, les arbres ne sont pas très beaux, car nous avons dû désactiver les lumières dynamiques afin d'améliorer la réactivité du jeu.

Accueil

Choix du pseudo et de l'adresse IP du serveur

Interface utilisateur

Son plus beau profil

Vue côté serveur de la map