Le développement de ce projet a été réalisé
sous Windows avec Visual C++.
Compte-tenu de la nature du programme, nous avons choisi d'utiliser le langage
C++ afin de pouvoir bénéficier des avantages de la programmation
objet, qui s'avère particulièrement bien adaptée aux
jeux, et ce malgré les limites imposées par l'architecture
C d'OpenGL.
Organisation :
L'application est composée de trois modules : le menu, le jeu du
Puissance 4 et le jeu Space Invaders.
En ce qui concerne OpenGL, pour assurer une cohérence lors du passage
d'un module à un autre, chaque module implémente une fonction
razOpenGL qui va remettre à leurs valeurs par défaut toutes
les lumières et matériaux utilisés.
Fichiers sources :
Les fichiers sources sont tous placés dans le même répertoire,
mais le préfixe de chaque fichier indique son appartenance à
l'un ou l'autre jeu : les fichiers appartenant à Space Invaders commencent
par le préfixe "SPA_" et les fichiers appartenant au Puissance
4 commencent par "PUI_". Les fichiers ne comportant pas un de
ces préfixes sont les fichiers du lanceur, du menu, et de la bibliothèque
"Module Manipulateur".
Fichiers de textures :
Les fichiers de textures utilisés par l'application sont au format
BitMaP (BMP) et sont placés dans le même répertoire
que les fichiers sources afin d'éviter tout problème de compatibilité
entre différents systèmes d'exploitation.
Compilation :
Le fichier Workspace de Visual C++ est inclus avec les sources afin de permettre
une compilation rapide et aisée sous Windows. Cependant, le projet
comporte également un fichier Makefile pour sa compilation sans Visual
C++.
Utilisation :
L'application démarre sur un menu permettant de choisir un des jeux.
Utilisez pour cela les flèches de direction pour amener au premier
plan le jeu désiré. Un appui sur la barre d'espacement lance
le jeu en question. Lors d'un jeu, vous pouvez à tout moment revenir
au menu grâce à la touche d'échappement ou au menu contextuel.
Un appui sur la touche F1 affiche à tout moment les touches à
utiliser.
Puissance 4 :
Principe :
Le jeu reprend le principe du Puissance 4 "classique" à
la différence qu'ici l'aire de jeu est en trois dimensions, et
il est donc possible de placer des billes en profondeur.
L'aire de jeu est décomposée en quatre grilles superposées.
Chaque grille est composée d'un carré de cases de coté
quatre. Chaque joueur, à son tour, dispose d'un curseur placé
au dessus de ces quatre grilles, qu'il peut déplacer pour se positionner.
Une fois positionné, le joueur lâche sa bille sur l'aire
de jeu, qui va tomber et atterrir sur la première case inoccupée.
Le premier joueur qui aligne quatre billes de sa couleur gagne!
Organisation :
Puissance 4 est composé de quatre classes :
PUI_JeuPuissance4, qui est la classe "principale" du jeu
du Puissance 4, c'est elle qui sera instanciée par le lanceur.
PUI_AireDeJeu, qui va d'une part dessiner l'aire de jeu et d'autre
part contenir les structures de données représentant
le jeu en mémoire.
PUI_Bille, qui va contenir les méthodes nécessaires
à l'affichage d'une bille sur l'aire de jeu.
PUI_Curseur, qui va matérialiser le curseur que le joueur
peut déplacer avant de lâcher une bille (classe fille
de PUI_Bille).
Détail des classes :
PUI_JeuPuissance4 :
Cette classe matérialise un jeu du Puissance 4. C'est elle
qui va s'occuper de tous les traitements inhérents au déroulement
d'une partie.
C'est aussi à elle qu'incombe la plupart des traitements OpenGL
:
définition des lumières.
affichage principal (fonction display).
capture des évènements claviers.
définition et changement du mode d'affichage (solide/filaire,
fenêtré/plein-écran) et des différentes
vues de caméra possibles.
affichage du texte (fin de partie ou aide)
PUI_AireDeJeu :
Cette classe matérialise l'aire de jeu, qui est implémentée
sous la forme d'un tableau d'entiers à trois dimensions. Cette
abstraction est simple à comprendre : un "0" signifie
que la case est libre, un "1" qu'une bille du joueur 1 y
est présente, et un "2" qu'une bille du joueur 2
y figure. Outre la simplicité de cette représentation,
il est également plus rapide de manipuler des entiers que des
objets.
La structure de données la plus importante du jeu étant
présente dans cette classe, divers traitements y sont effectués
:
L'ajout d'une bille dans l'aire de jeu (fonction ajoutePUI_Bille)
L'évaluation de la fin de la partie.
Celle-ci est réalisée au moyen de trois fonctions
:
finDePartie, qui va parcourir itérativement chaque
case de l'aire de jeu, et qui, pour chaque case contenant
une bille, va lancer la fonction chercheAlignement.
chercheAlignement, qui pour une case et une couleur donnée,
va lancer la fonction explore sur toutes les cases qui lui
sont connexes.
explore, qui va partir dans la direction donnée par
chercheAlignement, et renvoyer le nombre de billes de la même
couleur alignées dans cette direction.
Les fonctions d'affichage de la grille de l'aire de jeu ainsi que
les appels des fonctions d'affichage des billes pouvant s'y trouver
sont également prises en charge dans cette classe.
PUI_Bille :
Cette classe va contenir les différentes caractéristiques
d'une bille :
ses coordonnées dans l'aire de jeu
ses coordonnées d'affichage
le numéro du joueur auquel elle appartient
Elle va également permettre l'affichage d'une bille dans le
matériau correspondant à chaque joueur et dans le mode
idoine (solide/filaire).
PUI_Curseur :
Cette classe hérite de PUI_Bille, puisqu'un curseur n'est finalement
qu'une bille que l'on peut déplacer en x et en z.
Elle contient les méthodes d'affichage, qui sont des appels
aux fonctions correspondantes de la classe mère, ainsi qu'une
méthode permettant de changer les coordonnées x et z
du curseur dans l'aire de jeu en cas de déplacement.
Space Invaders :
Principe :
Vous dirigez un vaisseau spatial situé au bas de l'écran
(un X Wing pour être précis). Plusieurs rangées de
vaisseaux spatiaux aliens (des Tie Raptors) se déplacent horizontalement
en haut de l'écran et descendent d'un cran à chaque aller-retour.
Vous devez détruire ces aliens avant qu'ils n'atteignent le bas
de l'écran et vous touchent, et ce en évitant leurs tirs.
Le jeu est composé de cinq niveaux : "facile", "moyen",
"difficile", "encore plus dur" et "suicidaire".
Organisation :
Le jeu Space Invaders est composé de 6 classes et de 3 fichiers
:
Classes :
SPA_Ship, qui permet de gérer un vaisseau.
SPA_particule, qui permet de dessiner et de gérer une
particule.
SPA_Laser, qui permet de gérer un tir de vaisseau.
SPA_Explosion_x_wing, qui permet de gérer l'explosion
du vaisseau du joueur.
SPA_Explosion, qui permet de gérer l'explosion d'un vaisseau
ennemi.
SPA_JeuSpaceInvader, qui est la classe "principale"
du jeu Space Invaders, c'est elle qui sera instanciée par
le lanceur.
Fichiers :
SPA_TieRaptor, qui représente un vaisseau ennemi.
SPA_Tir, qui représente un tir de vaisseau.
SPA_x_wing, qui représente le vaisseau du joueur.
Détail des classes :
SPA_Ship :
Cette classe matérialise un vaisseau.
Elle va contenir les différentes caractéristiques d'un
vaisseau :
ses coordonnées dans l'espace.
savoir s'il faut afficher (dessiner) le vaisseau ou non
savoir si un vaisseau est vivant ou non (s'il est touché
ou pas)
le type d'un vaisseau (tie raptor ou x_wing).
Cette classe permet d'initialiser tous ces paramètres (méthodes
précédées du mot 'set') et de lire ces paramètres
(méthodes précédées du mot 'get').
SPA_particule :
Cette classe matérialise une particule.
Une particule est un point constitué :
de trois couleurs,
du canal alpha (pour gérer la transparence de la particule),
de trois coordonnées (pour gérer la particule
dans l'espace),
d'une vitesse.
Pour ce qui est de la couleur et des coordonnées, des initialisateurs
et des sélecteurs permettent de gérer ces paramètres.
La vitesse, quant à elle, est calculée aléatoirement
lors de la création de chaque particule.
Cette vitesse est donc différente pour chaque particule créée.
Enfin une méthode 'DrawParticule' permet de dessiner une particule
à l'écran en tenant compte des données sur la
couleur et des coordonnées.
SPA_Laser :
Cette classe contient les différentes caractéristiques d'un
tir.
Un tir est caractérisé par :
trois coordonnées,
un joueur (d'où provient le tir),
une variable qui indique si le tir est actif ou pas (à
afficher ou non).
Le fichier SPA_Tir permet de dessiner un tir de vaisseau alors que
cette classe SPA_Laser gère ce tir.
Cette classe n'est constituée que d'initialisateurs de sélecteurs.
SPA_Explosion_x_wing :
Cette classe matérialise une explosion de x_wing. Elle utilise
la classe SPA_particule.
Une explosion de x_wing est constituée de 500 particules.
Le constructeur de cette classe choisit une couleur pseudo-aléatoire
pour chaque particule et initialise le canal alpha à 1 (non
transparent) pour signaler que l'explosion n'est pas active.
Une méthode 'SetPosition' permet de signaler les coordonnées
initiales de l'explosion.
Ces coordonnées ne gèrent en aucun cas la position de
chaque particule.
Une méthode 'SetColor' permet de réinitialiser la couleur
de chaque particule.
La méthode 'DrawExplosion_x_wing' permet de dessiner une explosion,
c'est-à-dire de dessiner toutes les particules au point de
coordonnées initial où l'explosion doit se produire
et de dessiner chaque particule à l'aide de la méthode
'DrawParticule' de la classe particule.
Une autre méthode importante de cette classe est la méthode
'UpdateExplosion_x_wing' qui met à jour l'explosion. Mettre
à jour une explosion c'est en fait la propager.
Pour chaque particule, on recalcule une nouvelle position à
partir de l'ancienne et de la méthode 'GetSpeed' qui choisit
aléatoirement une nouvelle position pour la particule.
Cette méthode décrémente en même temps
le canal alpha pour rendre chaque particule un peu plus transparente
à chaque fois que la méthode 'UpdateExplosion_x_wing'
est appelée.
Ainsi, on donne l'illusion que la brillance de chaque particule s'estompe
avec le temps.
Lorsque le canal alpha est à 0, on remet la variable actif
à faux pour signaler de ne plus gérer les particule,
puisque l'explosion n'est plus visible.
SPA_Explosion :
Gère une explosion de Tie Raptor.
Cette classe est presque la même que la classe SPA_Explosion_x_wing
excepté qu'une explosion de Tie Raptor n'est constituée
que de 50 particules.
L'autre différence est que cette classe peut gérer 32
explosions à la fois car il y a 32 Tie Raptor.
SPA_JeuSpaceInvader :
Cette classe matérialise un jeu du Space Invader.
C'est elle qui va s'occuper de tous les traitements inhérents
au déroulement d'une partie. C'est aussi à elle qu'incombe
la plupart des traitements OpenGL :
définition des lumières.
affichage principal (fonction display).
capture des évènements claviers.
définition et changement du mode d'affichage (solide/filaire,
fenêtré/plein-écran) et des différentes
vues de caméra possibles.
affichage du texte (fin de partie ou aide)
Cette classe est composée d'un nombre important de méthodes.
Nous allons donc nous intéresser uniquement aux plus importantes
:
Pour commencer nous allons décrire comment l'univers
(l'espace) a été créé.
En réalité, c'est une sphère texturée
avec tous les vaisseaux à l'intérieur.
Ce stratagème permet de donner une impression de 3D lorque
l'on fait tourner la scène avec la souris.
Pour réaliser cette 'espace', deux méthodes sont
principalement utilisées.
La méthode Sphère, qui dessine une sphère
et précise les points d'attache de la texture.
Et la méthode 'LoadBMP' qui permet de charger une image
BMP en mémoire.
Pour dessiner les différents vaisseaux, on appelle les
méthodes 'Draw_x_wings', 'Draw_Tie_Raptor' et 'Draw_Tir'
contenues dans les fichiers SPA_x_wing, SPA_TieRaptor et SPA_Tir.
La principale méthode est celle appelée 'scene_jeu'
qui permet de gérer le déroulement de la partie.
Cette méthode est commentée pour comprendre son
fonctionnement.
Plus simplement, cette méthode réalise les opérations
décrites ci-dessous :
- vérifie si on est en fin
de partie. Si oui, alors effectue un test sur le nombre de vies
restantes pour savoir si c'est la fin du jeu (game over), ou si
on passe au niveau suivant.
- Si ce n'est pas la fin de partie, alors on vérifie si
l'on doit afficher le x_wing ou non.
- Vérifie si tous les Tie Raptor sont détruits ou
non,
- Pour chaque Tie Raptor, on le déplace s'il est vivant,
si on a atteint un bord, on fait descendre les Tir Raptor.
- On vérifie que les Tie Raptor n'ont pas atteint le X
Wing (les Tie Raptor ne sont pas en bas de l'écran).
- On choisit si un Tie Raptor doit tirer ou non.
- On gère chaque tir, en les faisant se déplacer
vers le bas pour les tirs de Tie Raptor et vers le haut pour les
tirs de X_wing.
- Si un tir est en contact avec un vaisseau, on lance une explosion.
En contact signifie avoir les même coordonnées à
plus ou moins 5%.
- Si tous les Tie raptor on été détruis,
on indique la fin de partie.
- On gère chaque explosion.
Une autre méthode importante est la remise à zéro
des lumières et matériaux utilisés.
Ainsi si on choisit l'autre jeu, il n'y aura pas de problèmes
de lumières ou de matériaux.
La remise à zéro est l'affectation des lumières
et des matériaux avec les valeurs par défaut d'openGL.
Cette méthode s'appelle 'razOpenGL'.
Les autres méthodes sont assez compréhensibles. Pour
ce qui est des variables utilisées, elles sont commentées
dans l'entête de la classe et ont des noms assez explicites.
Détail des fichiers :
SPA_x_wing
Ce fichier contient les méthodes qui permettent de dessiner
un vaisseau x_wing.
Deux types de vaisseaux x_wing ont été implantés.
D'une part un vaisseau en volume (méthodes précédées
du mot 'Solid') et d'autre part un vaisseau en fil de fer (méthodes
précédées du mot 'Wire').
Ce fichier utilise des listes pour faciliter et optimiser l'implantation
de certaines parties du vaisseau x_wing.
Par exemple une liste a été utilisée pour créer
les ailes. En effet le fait d'avoir 4 ailes identiques justifie le
fait d'utiliser une liste pour ne pas avoir à répéter
4 fois les mêmes instructions.
SPA_TieRaptor.
Ce fichier permet de dessiner un vaisseau de type Tie Raptor.
Comme le fichier SPA_x_wing, ce fichier dessine deux type de vaisseau
Tie Raptor. Un en fil de fer et l'autre en volume.
Des listes sont également utilisées pour éviter
la répétition de lignes d'instructions.
SPA_Tir.
Ce fichier permet de dessiner un tir de vaisseau.
Un tir n'est rien d'autre qu'un cylindre non fermé.
Remarques concernant la programmation :
Constructeurs :
On peut remarquer dans les différents modules que la majorité
des initialisations des variables membres des objets ne sont pas réalisées
dans les constructeurs mais dans des fonctions d'initialisation (init).
Cette approche semble étrange, car c'est le rôle du (ou des)
constructeur(s) d'initialiser un objet. Cependant, dans la mesure où
il est possible de sortir des différents modules et d'y revenir,
ces méthodes permettent de réinitialiser un objet. Sans
elles, il aurait été nécessaire de détruire
et de recréer l'objet pour le remettre à son état
d'origine.
Utilisation de fichiers au sein de Space
Invaders
Il peut être légitime de se demander pourquoi de "simples"
fichiers C sont présents dans le jeu Space Invaders, alors que
le reste de l'application est en C++. Tout d'abord, l'abstraction de ces
fichiers peut ne pas être considérée comme objet.
En effet, ces fichiers ne contiennent aucune caractéristique propre,
et leurs méthodes n'effectuent pas de traitements à proprement
parler ; il ne s'agit en fait que d'instructions de dessin. D'autre part,
il s'agit d'un choix conditionné par un souci de modularité.
En effet, les dessins de X Wing, de Tie Raptor et des tirs peuvent sous
cette forme être réutilisés dans un autre programme
de la manière la plus simple qui soit.