Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members

LIBELL Documentation

Introduction

Le but de ce document est de rapidement présenter l'architecture du programme. La documentation utilisateur est dans un autre fichier. J'ai utilisé Doxygen pour la documentation car il permet de parcourir rapidement le code grace à ses nombreux liens hypertextes. Visual Studio permet une approche semblable mais je prefere une documentation indépendante, plus agréable et rapide à parcourir.

Architecture globale

Ce programme est articulé autour des modules principaux suivants :

La classe cGame_World

Cette classe contient la description du niveau ( ensemble de murs dans une zone). Elle se base sur un arbre binaire de recherche construit à partir d'un ensemble de murs verticaux ou horizontaux. Pour cela on a d'abord développé une classe cBSP_Tree générique qui divise tous type d'ensemble de données que l'on peut diviser deux. Cette classe est templatée dans notre cas pour diviser un espace 2D ( donc des cPoint2D), avec comme diviseur des cHV_Wall ( murs horizontaux ou verticaux ).
On fois l'arbre construit, on le décore en mettant à la place de chaque feuille un objet cSquare_Area qui représente une piece rectangulaire.

Ensuite, chaque cSquare_Area construit la liste des murs qui la bordent effectivement. Elle detecte aussi les passages entre elle et les pieces voisines. Ces passages sont représentés par des objets cPortal. Un cPortal est donc qualifié par son segment porteur cPortal::Segment. Toutefois, on doit lui assigner une position exacte, on choisira le point milieu de ce segment. Cette solution n'est pas parfaite, notament lorsqu'on a un reseau dense de portails, mais elle convient pour ce petit jeu.

Puis on contruit la connectivité entre les objets cPortal. C'est à dire que chaque cPortal va avoir dans sa liste membre cPortal::Voisinage la liste des cPortal qu'on peut rejoindre en ligne droite sans rencontrer d'obstacle. On prends pour cela tous les cPortal des deux pieces qui bordent chaque cPortal.
Il reste toutefois un problème lorsque deux objets cPortal sont colineaires dans une meme piece cSquare_Area. Dans ce cas, la ligne droite entre ces deux portails va intersecter un mur. On va donc crée un objet cParalell_Portal entre ces deux portails. Il sera situé à égale distance des deux portails, mais décalé un peu dans la pièce pour que le chemin ne touche pas le mur. Ce portail en voisins que les deux cPortal paralelles qui l'ont générés.

La classe cA_Star_Search

la classe cA_Star_Search permet d'appliquer un algorithme A* sur un graph de noeuds c_A_Star_Node. La cA_Star_Node est purement virtuelle. L'algorithme permet donc tout type de recherche basé sur l'algo A*. Dans notre cas, les noeuds du graph seront des objets cPortal et cA_Star_Point. On a défini les cPortal dans la section precedente La classe cGame_World. Les cA_Star_Point sont des noeuds qu'on insere temporairement dans le graph et qui vont définier le point de départ et le point d'arrivée. Lorsque l'algorithme de recherche est terminé, on enleve ces noeuds du graphe.
La classe cGame_World précédement définie contient un objet cA_Star_Search.

La class cGame_Manager

La classe cGame_Manager est le moteur haut-niveau du jeu. Le fonctionnement est celui d'une FSM dont l'état est défini par la donnée membre cGame_Manager::Game_State. Les valeurs possibles sont celle de enum Game_State_Type:
-> INITIALIZING : etat du jeu initial, mis par l'appel au constructeur
-> RUNNING : un niveau est en train d'etre joué
-> READY_TO_START : un niveau ( ou une nouvelle vie) est pret a etre joué, on attends que le joueur le lance ( press space)
-> LEVEL_FINISHED : le joueur vient de terminer un niveau
-> VICTORY : le joueur vient de terminer le dernier niveau

Cette classe contient les elements suivants :

un objet cGame_World

On le charge avec un niveau grace à cGame_Manager::Load_Level(int i). Ce chargement est effectué grace à l'objet membre cLevel_Loader. Cette classe encapsule l'algorithme pour lire des niveaux.

un joueur

Il s'agit d'un objet cPlayer qui hérite de cUnit ( classe des unités mobiles) : cGame_manager::Player.

une liste des ennemis

Il s'agit d'une liste d'objet cEnnemy : cGame_Manager::Ennemies_List.

une liste des objets interactifs

Il s'agit d'une liste d'objet cInteractive_Item : cGame_Manager::Bonus_List.

un objet pour afficher des infos de debug à l'ecran

Pour vérifier la cohérence du niveau ( les cSquare_Area, leur murs et leurs portals, le reseau de cPortal, les chemins), on ajoute à la classe un objet de type cGame_World_Debug_Displayer. Cet donnée memebre cGame_Manager::World_Debug_Displayer permet d'envoyer à l'ecran les informations voulues. On trouve dans la documentation utiisateur les touches permettant d'afficher les infos souhaitées.

Executables

Le jeu se trouve dans le repertoire bin. Libell.exe est la version Release. Libell_debug.exe est la version debug. On trouve aussi dans bin l'éxécutable bsp_test qui permet de tester le bsp construit. Il teste les chemins construit dans un niveau, les intersections entre des segments et les murs, et enfin les intersections entre des boules et les murs.

Remarques

Tests

Les seuls tests automatiquent sont ceux de bsp_test.exe. Ces tests sont limités. Il aurait bien sure fallut les etendre. Mais cela n'etait pas possible dans les temps impartis. Un test qui pourrait etre efficace serait de generer des chemins aleatoires et de verifier ensuite que ces chemins n'intersectent pas les murs.

DirectX

J'utilise les classes des fichiers ddutil;h et dxutil.h. J'ai préféré blitter les boules plutot que de les dessiner, cela m'a parut plus rapide à coder et il fallait de toute facon du code pour blitter des surfaces pour les autres objets. Au final, le code pour l'affichage n'est pas rès élégant, mais pour terminer dans les délais, j'ai préféré affiner les parties sur le BSP, le A* et le game_manager que le code DirectX. C'est pourquoi le jeu ne se fenetre pas, les surfaces ne se restaurent pas, il n'y a qu'un mode d'affichage....

Optimisation

Mon idée de l'optimisation est de la commencer uniquement lorsque le programme est parfaitement stable, robuste... Le code présenté n'est donc pas optimisé.

Gameplay

Le jeu se révèle très plaisant à jouer meme s'il faudrait désigner mieux les niveaux. Notament, les arretes vives sont parfois très genantes pour diriger la balle. Il faurdait construire des niveaux qui les evitent. Les trois missions fournies donnent un bon apercu de la jouabilité.

Bugs

Les chemins des boules sont parfois à une distance inférieure à leur rayon d'un mur. Il arrive donc parfois qu'une boule empiete sur un mur en le longeant. Le problème n'est pas critique sauf dans le cas ou le joueur se croyait protégé de l'autre coté du mur, dans ce cas il peut se faire tuer.

Le BSP n'accepte pas les murs colinéaires. Si l'on veut en mettre dans le jeu, il faut bien veiller à ce que un autre mur aie precedement divisé la zone dans laquelle on va ajouter les murs paralleles.

Les boules vont plus vite lorsqu'elle se déplacent en diagonale, il faudrait pour eviter ca revoir la classe cIncremental_Bresenham.

Les cParalell_Portal sont parfois mal positionné. Il arrive parfois que deux portails de ce type ne soit pas séparés par un mur. Dans ce cas, on ne devrait pas créer un cParalell_Portal mais seulement relier les deux cPortal.

J'ai choisi d'utilisé le mode direct pour lire le clavier car cela est plus adapté pour lire le mouvement du joueur. On a juste besoin de savoir si une touche de direction es tenfoncée ou non. Mais pour poser des objets, cela n'est pas adapté car si le joueur appuie très rapidement sur une touche, mais au mauvais moment, le programme ne lira pas cette touche. Il faudrait utiliser le mode buffered de Direct Input pour ces touches.

TODO

Paramétrer le jeu pour s'afficher dans plusieurs résolution. Les samples du SDK donnent des sources pour lister les les possibilités d'affichages, on pourrait s'en servir.

La gestion des erreurs est très discutable. Il faudrait utiliser des interuptions pour rendre le code plus clair.

Utiliser le mot clé const. Ce n'est pas dans mes habitudes, mais c'est fournit par le C++, autant s'en servir...

J'ai tendance à rendre public la plupart des méthodes et des données membres. Il faudrait écrire plus de fonctions aux données membres et rendre ces derniers privés. Mais je trouve cela assez lourd de déclarer beaucoup de fonctions d'acces... ( mauvaise habitude !?) Une autre solution est de déclarer des classes amies.

Il faudrait aussi régler plus finement le gameplay des niveaux. C'est à dire reforcer la notion de pièces dans le design des niveaux, qui forcent le joueur à utiliser des teleporteurs pour ressortir de la pièce, et aussi à poser des bombes à l'entrée de la pièce.

Lorsque l'on est touché par un ennemi, on redémarre la vie suivante à la meme position et l'ennemi est renvoyé à sa position initiale. Or cette position initiale peut etre très proche voir meme à l'endroit exacte du joueur. Dans ce cas, impossible de survivre.... Il faudrait alors déplacer un peu le joueur ou l'ennemi.

La classe de chargement des fichiers cLevel_Loader détecte les balises inconnues ou une syntaxe mauvaise. Mais en raison des délais, le code n'est pas robuste à tout type d'erreur de syntaxe dans le fichier de niveau. Il faudrait le rendre plus robuste. De plus, on ne vérifie à aucun moment la cohérence de la carte lue. Il faudrait par exemple tester que les positions initiales des boules ne sont pas sur des murs.

Le cGame_World construit ne test à aucun moment sa cohérence. Il serait facile d'écrire quelques fonctions d'autotest pour cet arbre. Par exemple, on peut aisement tester que la somme des aires des cSquare_Area est égale à la somme totale de l'aire de jeu. On peut aussi tester qu'une fois décorés, tous les cPortal sont situés entre deux pieces,

Le BSP empile les murs dans l'ordre auquel on les lui donne. Donc les fichiers des niveaux doivent etre construit tels qu'ils essaient d'équilibrer l'arbre. Il faudrait qu'il soit capable de s'auto équilibrer.


Generated on Fri May 21 19:22:36 2004 for LIBELL by doxygen 1.3.7