Blog ENI : Toute la veille numérique !
-25€ dès 75€ sur les livres en ligne, vidéos... avec le code FUSEE25. J'en profite !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Design Patterns
  3. Représenter l’état du jeu
Extrait - Design Patterns Apprendre la conception de logiciels en réalisant un jeu vidéo (avec exercices et corrigés)
Extraits du livre
Design Patterns Apprendre la conception de logiciels en réalisant un jeu vidéo (avec exercices et corrigés) Revenir à la page d'achat du livre

Représenter l’état du jeu

Les données et leur représentation

1. Conception initiale

L’objectif à ce stade du projet est de trouver la meilleure manière de représenter l’ensemble des informations utiles pour le jeu. Bien qu’elle puisse paraître triviale, cette étape est primordiale et a des répercussions sur l’ensemble du projet, jusqu’aux dernières phases. Il convient donc d’y consacrer une énergie importante - tout en gardant à l’esprit qu’il n’est jamais possible de tout prévoir à l’avance.

Avant de se lancer dans des aspects purement informatiques, il est fortement conseillé de préparer ce travail sur le papier, en dressant la liste des informations utiles pour le jeu. Cela commence par la description sommaire de celles-ci, puis, la liste gagnant en taille, il est pertinent de commencer à faire des regroupements.

Ce travail peut être réalisé sur une feuille blanche si vous êtes un ou deux, sur tableau blanc si vous êtes trois à cinq. Au-delà, des techniques plus avancées de gestion de projet sont requises. Elles ne sont pas abordées dans cet ouvrage, et peuvent être trouvées dans le livre « UML 2.5 - Initiation, exemples et exercices corrigés », Fien VAN DER HEYDE - Laurent DEBRAUWE, Éditions ENI.

2. Types d’informations

Les types des informations requises pour représenter les données sont très nombreux. Parmi ceux-ci, il faut souvent choisir une manière de représenter la position d’un élément dans le monde. Dans le cas du jeu Pacman, cela se résume à des coordonnées 2D (x,y). Une notion de position relative peut également être nécessaire, comme dans le jeu Pacman où un personnage en mouvement peut être entre deux cases. Pour les jeux dont le monde...

Les informations élémentaires

Une fois les informations jetées sur le papier, on peut commencer à réfléchir à la manière de les représenter d’un point de vue informatique. À ce stade, il n’y a pas encore de programmation, mais un travail de conception sur la structure de la représentation.

1. Classes

Chaque application a des données propres, il n’est pas possible de proposer une recette unique pour tous les cas. Un certain nombre de constantes peuvent toutefois être trouvées. La première d’entre elles est la nécessité de représenter des informations élémentaires, comme les utilisateurs d’un système ou les différents types de personnages dans un jeu. Pour cela, l’approche naïve propose de fabriquer des représentations indépendantes les unes des autres. Avec ce type d’approche, les classes peuvent représenter un ou plusieurs types de données.

Pour concevoir ces classes, il existe plusieurs façons de faire. La première et la plus naturelle est de dessiner directement sur le papier. Ce vieux support peut sembler dépassé lorsqu’on se lance dans la conception logicielle, il n’en est rien ! Il demeure infiniment plus rapide de griffonner quelques classes et leur relation sur le papier, plutôt que de considérer les alternatives. N’hésitez donc pas à l’utiliser, et encore plus lorsque vous travaillez en groupe. Une fois que votre essai commence à converger, il devient intéressant d’utiliser des logiciels UML. Ceux-ci vous permettent de représenter vos classes d’une manière très propre, mais aussi de générer une partie du code source correspondant.

a. Créer un nouveau diagramme de classes avec NetBeans/EasyUML

Dans le cadre de cet ouvrage, le logiciel NetBeans avec le plugin EasyUML est utilisé. Pour créer des diagrammes de classes, il faut commencer par créer un projet UML.

 Cliquez sur le menu File - New project..., puis choisissez la catégorie UML et le type de projet UML Diagrams Project :

images/02RI001.png

 Cliquez sur le bouton Next.

 Saisissez le nom du projet dans la zone Project Name et son dossier dans la zone Project...

Les conteneurs

1. Les listes et tableaux associatifs

Une fois les informations élémentaires définies, il est nécessaire de les stocker dans des conteneurs (collections). Pour les cas où seul un faible nombre fixe d’éléments est attendu, de simples attributs dans une classe font l’affaire. Pour les autres cas, une structure de données est requise. Il existe de nombreuses possibilités, chacune ayant ses avantages et ses inconvénients.

a. Listes

Les listes permettent de stocker un nombre variable d’éléments en fonction d’un indice compris entre 0 et le nombre d’éléments moins 1. Ces indices sont obligatoirement continus : pour ignorer l’un des emplacements d’une liste, il faut y placer une référence nulle. Les deux principales opérations des listes sont l’ajout (add) et l’accès (get), toutes deux présentes dans l’interface java.util.List.

Il existe deux principaux types de listes : les tableaux (java.util.ArrayList) et les listes chaînées (java.util.LinkedList). Les deux permettent d’effectuer les mêmes opérations, mais diffèrent par leur rapidité d’exécution. Accéder à un élément d’un tableau est très rapide et ne dépend pas de sa taille. Par contre, ajouter ou retirer un élément est fonction de la taille du tableau : ajouter un nouvel élément dans un tableau peut donc être très long à exécuter. À l’inverse, les listes chaînées peuvent être modifiées très rapidement. Cependant, l’accès aux éléments est fonction de la taille de la liste : lorsque celle-ci est très grande, les accès sont très coûteux en calculs.

La création d’un tableau ou d’une liste peut être placée dans une référence à l’interface java.util.List à laquelle répondent les deux conteneurs :

List<String> list = new ArrayList(); 

ou :

List<String> list = new LinkedList(); 

Dans les deux cas, les méthodes d’accès sont les mêmes. Par exemple, la méthode add() permet d’ajouter un élément :...

Tests unitaires

1. Implanter des tests unitaires

a. Génération des accesseurs/mutateurs (getters/setters)

Pour générer les getters/setters, il existe deux solutions :

 Si vous avez la version améliorée de EasyUML : faites apparaître l’explorateur de diagramme easyUML en bas à gauche de NetBeans. Pour ce faire, cliquez sur l’onglet easyUML Explorer :

images/02RI036.png

 Sélectionnez la racine de l’arborescence affichée : dans l’exemple ci-dessus, elle est nommée pacman_state. Cela fait apparaître les propriétés du diagramme :

images/02RI037.png

 Pour les propriétés Getters et Setters, choisissez not public. Cela va générer les accesseurs/mutateurs pour tous les attributs non publics.

 Pour les classes World et WorldIterator, il est préférable de ne pas avoir de mutateur (setter) : l’utilisateur de ces classes n’a pas à modifier leurs attributs. Pour désactiver leur génération, cliquez sur une classe pour faire apparaître ses propriétés, puis sélectionnez disabled pour la propriété Setters, par exemple, pour la classe World :

images/02RI035.png

 Si vous n’avez pas la version améliorée d’EasyUML, générez les fichiers Java (cf. ci-dessous), puis pour chaque classe, faites un clic droit sur le nom de la classe. Cela fait apparaître un menu contextuel : cliquez sur Insert code.... Un deuxième menu contextuel apparaît : cliquez sur Getters and Setters.... Une boîte de dialogue apparaît : cochez tous les getters et/ou setters que vous souhaitez générer.

b. Générer le code source Java

Pour générer le code source Java :

 Il faut tout d’abord un projet Java (en plus du projet UML). Pour ce faire, cliquez sur le menu File - New project.... Dans la première page de l’assistant de création d’un projet, sélectionnez la catégorie Java, puis le type de projet Java Application.

 Cliquez sur le bouton Next.

 Dans la seconde page de l’assistant, saisissez un nom pour votre projet dans le champ Project Name. Sélectionnez un dossier dans le champ Project Location.

 Cliquez sur le bouton Finish.

 Dans l’explorateur de projet...

Solutions exercices

1. Exercice 2.4.1 : Jeu de rôle

Le diagramme suivant répond aux contraintes :

images/02RI041.png

D’une manière générale, une hiérarchie permet de tout représenter sans conflit ni duplicata.

Classe Hero : classe mère de toutes les autres, elle contient les propriétés communes (nom et niveau).

Classe Mage : représente les deux types de magiciens : prêtres et sorciers. Il n’est pas nécessaire de créer une classe pour chaque type de magicien, une énumération suffit.

Classe Bruiser : permet d’identifier les cogneurs, qui n’ont pas de mana.

Classe Warrior : représente un guerrier avec sa rage.

Classe Ranger : représente le rôdeur, accompagné de son familier. Le familier est une composition « 1..1 » vers la classe Pet, puisque selon la définition du jeu, ils sont liés à la vie à la mort. Dans le cas contraire, si le familier et le rôdeur pouvaient se séparer, une agrégation (losange blanc plutôt que noir) aurait été pertinente.

Classe Pet : étant donné qu’il possède les mêmes propriétés que n’importe quel personnage, on peut le faire hériter de Hero. Une autre solution aurait été d’hériter de la classe Bruiser, ou même Warrior, si ses mécaniques sont similaires. Il n’y a pas assez d’information dans la description du jeu pour le savoir, il pourra être intéressant de la retravailler.

2. Exercice 2.4.2 : Jeu de rôle avec classes multiples

Le diagramme suivant répond aux contraintes :

images/02RI042.png

Pour répondre aux critères multiples, nous avons suivi la première approche présentée, qui n’utilise que des interfaces. Il est également possible de suivre l’une des deux autres approches avec la composition.

Classe Hero : classe mère principale, contient les propriétés communes.

Interface IMage : interface pour la propriété de mana.

Interface...