Fil d'Ariane
Mis à jour le mardi 7 mars 2017
  • Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

TP: Pokemon, attrappez-les tous !

Connectez-vous ou inscrivez-vous pour bénéficier de toutes les fonctionnalités de ce cours !

Nous voilà déjà arrivés au dernier chapitre de la première partie de ce tutoriel. Il s'agit en fait d'un grand TP dans lequel vous utiliserez toutes les connaissances acquises tout au long des de ce tutoriel. Vous êtes surs d'avoir compris toutes les notions de programmation orientée objet ? Êtes-vous sûrs d'être capable de combiner les classes, l'encapsulation, l'héritage, les types membres et le polymorphisme afin de créer vos propres programmes ? C'est ce TP qui vous permettra de localiser vos lacunes (s'il y en a) et surtout de créer votre premier mini-projet.

Enoncé

La première étape dans la réalisation d'un programme objet est de se poser la question : de quoi on parle ? La réponse à cette question va nous aider à déterminer les classes (/singletons/traits) qu'on doit créer. Ici, dans notre jeu de Pokémon, on peut déjà dire qu'on aura besoin d'une (ou plusieurs) classe(s) pour les Pokémon et une classe pour la gestion du jeu. Ce n'est pas tout ce qu'on a besoin de créer mais c'est un bon point de départ.

Les Pokémon

Image utilisateurMaintenant, la question poser est : qu'est ce qui caractérise un Pokémon et qu'est ce qu'il est susceptible de faire. On ne va pas implémenter toutes les fonctionnalités qu'on trouve dans les jeux commerciaux (mais on en fera une bonne partie quand même). Voici donc ce que possèderont nos Pokémon :

  • type(s): tout Pokémon possède un ou deux types (feu, dragon, psy ...).

  • des points de vie : Ils varient entre 0 et PV_MAX. PV_MAX dépend du Pokémon lui même (un Pichu n'a pas les mêmes points de vie (PV) qu'un Celebi, mais on supposera pour ce TP que deux Pichu qui ont le même niveau ont le même PV_MAX) ;

  • défense, défense spéciale, attaque, attaque spéciale : des valeurs entières ;

  • des attaques : le nombre des attaques varient entre 1 et 4. Si vous n'arrivez pas à implémenter ce critère vous pouvez choisir un nombre constant d'attaques (par exemple 2) ;

  • un objet : un Pokémon peut tenir un objet. Comme pour les attaques, si vous n'arrivez pas à l'implémenter correctement supposez que tout Pokémon doit tenir un (et un seul) objet. Pour ce TP, on va créer un seul type d'objet qui est les baies.

On a simplifié pas mal de choses, mais ça fait déjà plein de trucs à coder (et ça ne sera pas aussi simple que vous ne le pensiez).
Autre que les accesseurs/mutateurs, la classe Pokémon dispose d'une seule méthode "attaquer" qui prend en argument le Pokémon à attaquer et le numéro de l'attaque (entre 1 et 4).
Si on repose désormais la question "de quoi on parle ?" on peut trouver de nouvelles classes qui seront nécessaires pour la gestion des types, des attaques et des objets.

Les attaques

Image utilisateurUne attaque possède un nom, un type et une précision et une puissance. La classe Attaque disposera d'une méthode qui permet d'attaquer un Pokémon. Il faut savoir que si un Pokémon de type Feu utilise une attaque de même type, la puissance de cette dernière sera multipliée par 1.5. Une attaque peut échouer en fonction de sa précision (vous pouvez vous rendre au TP Mario pour la gestion de la chance).
Il existe plusieurs catégories d'attaques : les attaques physiques et les attaques spéciales. Chacune de ces deux catégories correspond à des propriétés spécifiques du Pokémon attaquant et du Pokémon cible :

  • attaque physique : les dégâts sont calculés en fonction de l'attaque du Pokémon attaquant et de la défense du Pokémon attaqué ;

  • attaque spéciale : les dégâts sont calculés en fonction de l'attaque spéciale et de la défense spéciale.

Image utilisateur

objets et types

Il n'y a pas grand-chose à dire à propos ces deux classes. Les listes des objets qu'on va implémenter sera donnée plus bas. Comme je vous ai dit, seuls les baies seront implémentés. Lors d'un combat, un Pokémon dont les PV sont très bas (PV inférieurs à 25% de PV_MAX) peut manger une baie pour augmenter ses PV (en réalité il y a d'autres types de baies, mais on va se contenter des baies qui augmentent les PV).

Image utilisateur

La classe Jeu

C'est cette classe qui prendra en charge la gestion de tout le jeu ainsi que l'affichage du déroulement du jeu dans la console (ne mettez AUCUN println dans les autres classes).
Pour simplifier le jeu, on va créer un mode deux joueurs au lieu d'un mode joueur vs ordinateur. Lorsqu'on commence un nouveau jeu, on demande au premier joueur son nom et on lui délivre une somme d'argent. Le joueur doit ensuite utiliser cet argent pour acheter trois Pokémon, des attaques ainsi que des baies. On fera ensuite la même chose pour le deuxième joueur. Chaque joueur sera bien sûr une instance d'une classe Joueur que vous devez créer. Une fois les configurations (achats) terminés, le combat commence. Le combat sera composé par 3 "round" (le premier Pokémon du joueur 1 vs le premier Pokémon du joueur 2, et ainsi de suite). Chaque joueur choisit une attaque, et c'est le Pokémon ayant la plus grande vitesse qui attaquera en premier. La manche se termine si le Pokémon est K.O. (PV == 0) ou s'il ne peut plus utiliser d'attaques (les PP de tous les attaques sont nuls). Le joueur qui gagnera au moins deux manches sera déclaré vainqueur.

Image utilisateur

Précisions et indications

Vous n'allez pas créer des centaines de classes pour représenter tous les Pokémon et les attaques. Choisissez 5 ou 6 Pokémon et une dizaine d'attaques pour les implémenter. On verra dans la deuxième partie comment utiliser les collections, les fichiers et les fonctions pour générer tout ça sans avoir à écrire mille classes.
Pour les types, vous devez implémenter au moins ceux correspondants aux Pokémon et attaques que vous avez choisi.
Enfin, pour les baies, il y aura la baie Oran qui augmente les PV de 20 (vous pouvez en ajouter d'autres si vous voulez).
Je vous laisse la liberté du choix des prix des différents éléments. Je ne vais pas non plus vous donnez de formules pour le calcul des dégâts, soyez créatifs tout en respectant les contraintes que je vous ai donné.
PS: si vous n'avez pas suffisamment de connaissances sur les Pokémon, vous pouvez chercher sur google, ou même inventer vos propres Pokémon et attaques.

Pourquoi t'es devenu méchant dans ce TP ? :'(

Vous n'êtes plus des débutants maintenant et je suis certain que vous avez le niveau nécessaire pour faire ce TP. En plus, rien ne vous oblige de tout implémenter, commencer par une implémentation simple et ensuite ajoutez les fonctionnalités avancées. N'oubliez pas que le jour où vous déciderez de faire votre propre jeu, personne ne sera là pour vous guider. Enfin, n'oubliez pas que si vous avez des problèmes vous pouvez toujours créer un sujet sur le sous-forum Autres langages.
Bonne chance et surtout bon travail.

Image utilisateur

Éléments de correction

Contrairement au premier TP, je ne vais pas fournir une correction pour celui-là, et ceci pour diverses raisons :

  • il n y a ni une unique ni une meilleure correction pour le TP ;

  • la majorité des classes/méthodes qui doivent être écrites sont simples et basiques ;

  • c'est un bon test de vos connaissances, il est temps de savoir si vous avez bien assimilé et surtout si vous êtes capables d'appliquer correctement les diverses notions de la programmation objets ;

  • lorsqu'on se bloque et qu'on a la correction sous la main, on a tendance a jeter un coup d'œil à la correction au lieu dé réfléchir et d'essayer de se débrouiller.

Par conséquent, je vais me contenter de vous donner quelques indices et idées d'implémentation concernant les parties les plus "intéressantes" du TP.

Et comment serais-je capable d'évaluer mon code ?

Vous pouvez poster vos codes sur le forum (pas de code dans les commentaires du tutoriel s'il vous plait), mais d'abord, vous devez faire une auto-évaluation. Voici quelques questions que vous devez poser à la fin du projet :
est-ce que mon programme fait exactement ce qui est demandé dans l'énoncé (faites des tests pour s'assurer du bon fonctionnement du programme) ?

  • est-ce que j'ai respecté le principe d'encapsulation (mutateurs/ accesseurs, visibilité, ...) ?

  • est-ce que mon code est propre (nom de variables claires, pas de code redondant, ...) ?

  • est-ce que j'ai bien utilisé le concept d'héritage ?

  • est-ce il y a des classes qui ne servent à rien ?

  • est-ce qu'il y a des méthodes qui peuvent être réduites ?

  • ...

Bref, si vous êtes en train de lire cette partie rien que pour faire un copier/coller dans votre IDE et dire "Wouhou j'ai réussi le deuxième TP !", je vous conseille fortement d'arrêter la programmation de faire plus d'efforts et même de relire quelques chapitres du tutoriel. Sinon, si vous avez réussi le TP, vous pouvez continuer la lecture. :)

Hiérarchie globale

Je parie que plusieurs d'entre vous se sont dits en lisant l'énoncé du TP "Tiens, ça ressemble pas mal aux animaux !". En fait, on peut créer une classe générale Pokémon (<=> Animal), de laquelle héritent plusieurs classes (plutôt traits) comme PokemonFeu ou PokemonPsy (<=> Carnivore et Herbivore) et puis faire hériter de ces dernières les classes concrètes des différents Pokémon comme Mewtwo (<=> Chien, Serpent ...). Je suis certain que plusieurs d'entre vous ont opté pour cette implémentation, et je les félicite, ça prouve qu'ils ont bien suivi le chapitre sur l'héritage. Une autre possibilité est de créer la classe de base Pokémon et des traits indépendants pour les types :

trait PokeType
trait Feu extends PokeType
trait Psy extends PokeType
trait Dragon extends PokeType

// Puis pour les classes des Pokémons
class Dracaufeu extends Pokemon with Feu
class Dialga extends Pokemon with Dragon with Psy

L'avantage de cette implémentation est qu'on peut utiliser les types (Feu, Dragon ...) pour autre chose que les Pokémon, comme par exemple les attaques. Par contre, un inconvénient est dès qu'on a besoin de faires des trucs spécifiques dans les corps des traits (par exemple gérer les relations entre les types) on se trouve obligé de séparer les types pour les Pokémon et pour les attaques. Comme vous pouvez voir, chaque solution à ses avantages et ses inconvénients, il faut bien réfléchir avant de se lancer dans du code, et n'hésitez pas à changer l'implémentation au moment où vous trouvez une autre meilleure.

Les attaques et les objets

Une des plus difficiles fonctionnalités à coder est le nombre variable des attaques et objets. La meilleure façon de coder ceci est d'utiliser une collection, mais puisqu'on les a pas encore vu il va falloir se débrouiller autrement. L'idée est de créer un singleton fils du trait PokeAttaque qui ne représente aucune attaque, mais qui signifie l'absence d'attaque :

object AucuneAttaque extends PokeAttaque

Ensuite, avant d'utiliser une des attaques d'un Pokémon, il suffit de vérifier si celle si est différente de AucuneAttaque :

if (pikachu.attaque1 != AucuneAttaque) pikachu.attaquer(dialga, 1)

De cette même manière on peut implémenter le nombre variable (0 ou 1) des objets.

Gestion des joueurs

Dans la boucle principale du jeu, vous devez changer de joueur à chaque fois, soit en répétant le code pour chaque joueur dans le corps de la boucle (qui est la mauvaise solution) soit en utilisant une variable joueurActuel qui change de valeur à chaque itération (celle-ci est ok). Je vais vous présenter ici une nouvelle méthode. Nous allons créer une classe Alternance qui gérera l'alternance des joueurs. Son implémentation est simple :

class Alternance(val joueur1: PokeJoueur, val joueur2: PokeJoueur) {
  private var joueurActuel = joueur1
  def alterner = if (joueurActuel == joueur1) joueur2 else joueur1
  def joueur = joueurActuel
}

À première vue, la classe est complétement inutile puisqu'elle complique les choses. L'avantage de l'utilisation de cette approche est qu'on peut à tout moment changer l'implémentation de Alternance pour gérer un nombre différent de joueurs sans changer presque aucune ligne de code des autres classes. En fait, si vous voulez ici gérer plusieurs modes vous pouvez faire :

abstract class Alternance {
  def alterner 
  def joueurActuel 
}
class Alternance2(val j1: PokeJoueur, val j2: PokeJoueur) extends Alternance { /* */ }
class Alternance3(val j1: PokeJoueur, val j2: PokeJoueur, val j3: PokeJoueur) extends Alternance { /* */ }

(Vous devez bien sûr implémenter les deux méthodes dans les classes filles)

Conclusion

Comme dit plus haut, seuls les fonctionnalités les plus difficiles ont étés expliquées. Je m'excuse si certains d'entre vous se sont venus ici rien que pour trouver un code complet et le montrer à ses amis.

Et voilà le deuxième TP terminé. Les idées d'amélioration ne manquent pas ici, puisqu'on a implémenté une petite partie du monde des Pokémon. Amusez-vous à améliorer votre premier projet (vous devez en être fiers, n'est-ce pas ?).

Exemple de certificat de réussite
Exemple de certificat de réussite