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 : La malédiction de Mario

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

Salut,

Bienvenue dans le premier TP de ce tutoriel. Les TP sont une bonne occasion pour appliquer toutes les notions qu'on rencontre dans le tutoriel.

On va commencer par quelque chose de très simple : un jeu de Mario en console !

Bonne lecture et bon travail. ;)

Super Mario

Image utilisateur

Si vous êtes un habitant de cette planète, vous devez connaitre le personnage des jeux vidéo, Mario. Ce plombier italien est facilement reconnu avec sa salopette bleue, sa casquette rouge et ses moustaches bien particulières. Il a été créé par le Japonais Shigeru Miyamoto en 1981.

La première apparition de Mario fut dans le jeu Donkey Kong (sous le nom de Jumpman), mais son vrai succès a été achevé avec la série « Super Mario ». Depuis, il a été le héros dans plus de 200 jeux vidéo.

Mario vit dans le Royaume Champignon avec son frère Luigi, ses amis Toad et Yoshi et la princesse Peach. Son principal ennemi est le dragon Bowser qui essaye sans cesse de kidnapper la princesse, heureusement Mario est là pour sauver le jour.

Image utilisateur

Le TP

On va dans ce TP créer un simple jeu de Mario en console. Voici l'image de notre mini-stage :

Image utilisateur

Pour gagner, Mario doit :

  • sauter ;

  • resauter ;

  • battre un ennemi ;

  • entrer dans le château ;

  • vaincre Bowser ;

  • sauver Peach.

C'est exactement le jeu que je vous ai montré dans le début du chapitre 3. :D

C'est ce que vous êtes capable de faire ( :-° ) et il y aura du boulot quand même. Râlez comme vous voulez, mais vous ne trouverez jamais un tutoriel pour débutants qui commence (ou finit) avec un MMORPG en 3D.

Le TP se divise en deux parties :

  • d'abord on va créer des classes générales qui vont représenter tout personnage d'un jeu Mario-like ;

  • on va ensuite créer notre jeu : Mario et le jour de malchance.

Classes nécessaires

On va créer quatre classes : Personnage, Ennemi, Boss et Princesse.

Pourquoi ne pas les nommer Mario, Peach, etc. ?

Je vous ai dit une fois que lorsqu'on programme on ne veut pas répéter le code, on préfère écrire un code une fois et qu'il soit utilisable pour plusieurs cas différents. Pour rester dans cet esprit, on va créer des classes générales qui serviront non seulement pour notre Mario, mais pour tout jeu qui lui ressemble. On va d'ailleurs créer quelques champs (méthodes et variables membres) qu'on ne va pas utiliser. Ils seront là pour un éventuel autre jeu.

Voici ce que doit faire chacun des personnages secondaires :

  • les objets de classe Ennemi doivent être capables de battre seulement le personnage principal (qui est une instance de PersonnagePrincipal) ;

  • par contre, les objets de classe Boss doivent être capables de battre le personnage principal et les autres ennemis et boss ;

  • les princesses, quant à elles, doivent savoir donner un bisou et donner une gifle. Ça suffira amplement. :p

Le personnage principal est plus intéressant à créer puisqu'il est capable de faire plusieurs choses :

  • il a un nombre de vies et une quantité d'argent (les deux sont des valeurs entières) ;

  • il peut gagner et perdre de l'argent ;

  • il peut seulement perdre des vies (pas de mots de passe :pirate: ) ;

  • il peut battre les ennemis et les boss ;

  • il peut donner des bisous aux princesses ;

  • il peut entrer dans un château ;

  • il peut mourir.

Il s'agit de classes et de méthodes simples, vous ne devez pas avoir de problèmes de ce côté-là (je vais quand même vous guider un peu :ange: ).

L'histoire du jeu

Voici l'histoire du jeu, racontée par Toad :

« C'était un beau jour de printemps, tout le monde dans la Ville Champignon était en train de faire son travail avec euphorie et fierté. Tout d'un coup, tout devint noir, comme s'il y avait une éclipse. J'ai levé la tête vers le ciel et... surprise ! Ce n'était pas une éclipse, mais un géant château volant, bien évidemment celui de Bowser ! Tout le monde criait, courait dans tous les sens en hurlant « Mariooooo ! Marioooo ! ». Mais hélas, Mario a quitté la ville depuis un moment et est allé à Hollywood pour travailler comme garde du corps de Hannah Montana. :'( Je me suis caché dans une cave avec trois autres citoyens. Quelques heures après, je suis sorti voir ce qui se passait : c'était le chaos total ! Tout est détruit : les maisons, les arbres, les champs... J'ai couru vers le château de Peach, mais, malheureusement elle n'était pas là : Bowser l'a kidnappée. :waw:

À l'autre bout du monde, Mario était en train de se préparer pour le grand concert de Hannah. Il regardait son beau visage dans un petit miroir qu'il avait dans sa main, tout en chantant une des chansons de Hannah :

You get the best of both worlds
Chill it out, take it slow
Then you rock out the show

You get the best of both worlds
Mix it all together and you know that it's the best of both worlds

Quelques minutes avant le début du concert, Mario a reçu un SMS sur son iPhone 3G, qui contenait quatre mots : « Bowser a pris Peach ! ». Sous le choc, Mario a jeté le miroir par terre et couru vers l'aéroport pour retourner à la Ville Champignon. Le pauvre ne savait pas ce qui l'attendait, puisque briser un miroir entraîne sept ans de malchance ! >_ »

Déroulement du jeu

Le reste de l'histoire, c'est le destin qui va l'écrire. Dans le jeu du chapitre 3, tout était parfait, Mario faisait ce qu'il devait faire et sauvait sa princesse. Ceci se passe de cette façon si Mario est dans des conditions normales, mais pas avec une malchance qui le suit !
Désormais Mario a :

  • une chance sur cinq de tomber lors du premier saut ;

  • une chance sur trois de tomber dans le deuxième trou :

  • une chance sur quatre d'être tué par le premier Goomba ;

  • trois chances sur dix de pouvoir entrer dans le château sans qu'il ne soit repéré par les gardiens ;

  • une chance sur deux de battre Bowser (ça va être dur pour notre petit Mario) ;

  • une chance sur trois qu'il soit giflé par Peach (elle est jalouse de Hannah :p ).

Mario commence avec 10 vies. À chaque fois qu'il tombe, qu'il est attaqué ou qu'il est giflé, il perd une vie. Il a deux essais pour entrer dans le château, si aucune de ses deux tentatives n'est fructueuse il perd une vie. À chaque fois, Mario reprend le jeu depuis le début.

C'est simple ( :-° ), vous devez créer une classe nommée « JourDeMalchance » qui contient des variables représentant tous les personnages du jeu et une méthode « commencerUnJeu » qui s'occupera de tout le jeu. Vous pouvez ajouter d'autres méthodes intermédiaires si vous voulez.

Je ne vais pas vous laisser seuls, il y aura sûrement quelques indications utiles (eh oui, je suis gentil parfois :D ).

Indications

Les classes
  • Dans les quatre classes citées plus haut, vous devez mettre un champ « nom ».

  • Chaque méthode affiche du texte avec println et utilise les noms des différents personnages, par exemple :

    def donnerBisou(p: Princesse) = println(nom + " a donné un bisou à " + p.nom)
  • Les méthodes battre(p: Personnage) des classes Ennemi et Boss ainsi que la méthode donnerGifle(p: Personnage) de la classe Princesse doivent retrancher 1 aux points de vie (PV) du Personnage passé en argument.

  • La méthode mourir prend en argument une chaine et l'affiche à l'écran (utilisez cette méthode lorsque Mario tombe ou lorsqu'il est attrapé par les gardiens du château).

La chance

La gestion de la chance est un point important du jeu. On va utiliser une petite astuce : par exemple, pour le cas du premier saut, on a une chance sur cinq de tomber. L'astuce est de générer un nombre aléatoire entre 0 et 4 (on a donc 5 cas : 0, 1, 2, 3 et 4). Si le nombre tiré est 0 alors Mario n'a pas de chance et il tombe, sinon il saute avec succès.

Voici un autre exemple : pour une chance de 3 sur 10 on va générer un nombre aléatoire entre 0 et 9 (donc 10 possibilités). Si le nombre tiré est parmi 0, 1 et 2 (3 chances parmi les 10 possibles) on considère que Mario n'a pas été chanceux.
Pour générer le nombre aléatoire on va utiliser la classe scala.util.Random. On doit instancier la classe avec new et appeler la méthode nextInt sur l'objet créé. La méthode nextInt prend en argument un entier « n » et génère un nombre (pseudo) aléatoire entre 0 et (n - 1).

val rand = new scala.util.Random
rand.nextInt(10) // renvoie un Int entre 0 et 9 == (10 - 1)

Pour ne pas encombrer votre code, faites une méthode qui donne si on a une chance de n sur m ou pas (la méthode renvoie true si on a de la chance et false sinon).
Si vous n'arrivez pas à faire cette méthode par vous-mêmes, vous pouvez la trouver ici (essayez d'abord de la faire tout seuls, le copier / coller ne sert à rien) :

def chance(n: Int, m: Int) = {
    if (rand.nextInt(m) >= n ) true else false 
  }

Dans l'exemple des 3 chances parmi 10, observez que si le nombre tiré est strictement inférieur à 3 (0, 1 ou 2) alors on n'a pas la chance. C'est exactement cette idée que j'ai utilisée pour écrire la méthode chance : si le nombre tiré est supérieur ou égal à n, alors on renvoie true, sinon on renvoie false.
Notez que la condition if-else est superflue, si la condition est VRAI on retourne VRAI et sinon on retourne FAUX. Le plus logique est de retourner directement la condition :

def chance(n: Int, m: Int) = {
    rand.nextInt(m) > n  
  }
La méthode maledictionDeMario

Cette méthode contrôle tout le jeu. D'abord, elle donne les noms exacts aux variables qui représentent les joueurs (mario, peach, goomba et bowser). Ensuite le jeu commence : le déroulement du jeu se répète lui-même donc pensez à utiliser une boucle. Créez dans la classe jeux une variable booléenne gameOver initialisée à false et changez-la en true dès que Mario arrive à donner un bisou à Peach ou que ses PV atteignent zéro.
Pour le déroulement du jeu, pensez à des if-else imbriqués.

Affichage

Pensez à séparer les différents essais par des retours à la ligne ou autre. La méthode « * » de la classe String vue dans le chapitre 2 peut vous aider.
Pour que l'affichage de tout le jeu ne soit pas instantané, vous pouvez faire des pauses entre les instructions. Il suffit d'utiliser la méthode Thread.sleep :

Thread.sleep(1000) // fait une pause d'une seconde

L'argument de sleep est le temps en millisecondes (1000 millisecondes = 1 seconde).

Où est le new ici ? :o

Il n'y en a pas, je vous expliquerai pourquoi dans le chapitre suivant (et je vous expliquerai aussi pourquoi println est appelée sans objet ;) ).

Bon travail.

Correction

J'espère que vous avez réussi votre premier TP. Ne courrez pas à la correction lors du premier problème rencontré. Essayez tout ce qui vous passe par la tête, relisez l'énoncé et les indications et postez sur le forum (dans ce cas, même si vous n'arrivez pas encore à faire le TP, essayez de bien comprendre la correction et refaites l'exercice quelques jours plus tard ;) ).

Voici une solution possible (il n y a jamais une seule solution) :

class Personnage {
  var pv = 10
  var argent = 100
  var nom = "Héro"

  def sauter = println(nom + " a sauté !!!")

  def donnerBisou(p: Princesse) = println(nom + " a donné un bisou à " + p.nom)

  def battre(e: Ennemi) = println(nom + " a battu " + e.nom)

  def battre(b: Boss) = println("Wahouuuu !! " + nom + " a battu " + b.nom)

  def entrerChateau = println(nom + " est entré dans le château !")

  def mourir(s: String) = {
    println(s)
    pv -= 1
  }
}

class Ennemi {
  var nom = "Ennemi"

  def battre(p: Personnage) = {
    println(p.nom + " a perdu devant " + nom + " !")
    p.pv -= 1
  }
}

class Boss {
  var nom = "Boss"

  def battre(p: Personnage) = {
    println(p.nom + " a perdu devant " + nom + " !")
    p.pv -= 1
  }
  def battre(e: Ennemi) = println(e.nom + " a perdu devant " + nom + " !")
  def battre(b: Boss) = println(b.nom + " a perdu devant " + nom + " !")
}

class Princesse {
  var nom = "Princesse"

  def donnerBisou(p: Personnage) = println("mwaaaaaaaaah !")
  def donnerGifle(p: Personnage) = {
    println("Va-t-en !!")
    p.pv -= 1
  }
}

class Jeu {

  val mario = new Personnage
  val goomba = new Ennemi
  val bowser = new Boss
  val peach = new Princesse

  val rand = new scala.util.Random

  def chance(n: Int, m: Int) = {
    rand.nextInt(m) >= n
  }

  var gameOver = false

  def maledictionDeMario = {
    mario.nom = "Mario"
    goomba.nom = "Goomba"
    bowser.nom = "Bowser"
    peach.nom = "Peach"
    
    for(i <- 1 to 10; if (!gameOver)){
      println("*" * 50)

      if (chance(1, 5)) {
        mario.sauter

        if(chance(1, 3)) {
          mario.sauter

          if(chance(1, 4)) {
            mario.battre(goomba)

            if(chance(3, 10) || chance(3, 10)) { /* on a deux essais pour le château, il faut
                                                que le premier OU le deuxième soit VRAI */
              mario.entrerChateau

              if(chance(1, 2)){
                mario.battre(bowser)

                if (chance(1, 3)) {
                  mario.donnerBisou(peach)
                  gameOver = true  // si Mario arrive à baiser Peach alors le jeu se termine
                
                } else peach.donnerGifle(mario)

              }else bowser.battre(mario)

            }else println(mario.nom + " a été attrapé !")

          }else goomba.battre(mario)

        }else println(mario.nom + " est tombé dans le 2e trou !")

      }else println(mario.nom + " est tombé dans le premier trou !")

      if(mario.pv == 0) gameOver = true // si les vies de Mario sont toutes perdues on perd le jeu
      Thread.sleep(3000)  // temps d'attente 3 secondes
    }
    println("*" * 50)
    println("*" * 50)
    println("Game Over")
  }
}

Avant de vous laisser je vais vous donner quelques idées d'améliorations :

  • ajoutez d'autres obstacles ;

  • améliorez l'affichage avec des trucs comme :

            <**************************************
            *oo*oo****oooo****oooo****ooo****oooo*
            *o*o*o****o**o****o**o*****o*****o**o*
            *o***o****oooo****oo*******o*****o**o*
            *o***o****o**o****o*o******o*****o**o*
            *o***o****o**o****o**o****ooo****oooo*
            **************************************
  • faites un mode deux joueurs Mario/Luigi. Dès que l'un des deux est mort on passe au suivant jusqu'à ce que les deux gagnent, que les deux perdent ou que l'un gagne et l'autre perde ;

  • faites un mode défi à deux joueurs : Mario et Luigi répètent le jeu indéfiniment jusqu'à ce que l'un des deux gagne le jeu ;

  • trouvez d'autres idées d'améliorations.

C'est tout pour ce TP, on reviendra à la POO dans le prochain chapitre : « Constructeurs et Encapsulation ».

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