Projets SCRATCH débutants-intermédiaires
Gobtou
Description du projet
Le principe du jeu Gobtou est basé sur celui du jeu Pacman. Le joueur se retrouve enfermé dans un labyrinthe avec quatre ennemis. Le but du jeu est de ramasser tous les bonus disséminés dans les couloirs sans se faire attraper par les ennemis. Le joueur possède trois vies au début. Chaque fois qu’un ennemi le touche, il perd une vie. Le jeu se termine quand le joueur a ramassé tous les bonus ou quand il n’a plus de vie.
Ce projet est une version améliorée du labyrinthe. Il va falloir gérer les déplacements des ennemis. Nous utiliserons un système de déplacement aléatoire, à chaque intersection, la prochaine direction empruntée par le lutin sera tirée au hasard.
Le déplacement du joueur va être programmé de manière un peu plus complexe. Lorsqu’il presse une touche, son lutin va se déplacer dans la direction demandée jusqu’à ce qu’il rencontre un mur, même s’il lâche la touche de direction. Pendant le déplacement, si une touche de direction différente est pressée, nous testerons s’il peut aller dans cette direction tant que cette touche restera pressée.
Partie 1 – Préparation de l’application et ajout des ressources
Supprimer le lutin Scratch par défaut.
Ajouter le lutin “lutin_joueur_gobtou.sprite2” et “lutin_ennemi_gobtou.sprite2” et “lutin_murs_gobtou.png” fournis dans les ressources.
Dupliquer le lutin “ennemi” trois fois afin d’avoir quatre ennemis au total. Nous utiliserons quatre lutins identiques et non des clones car le but du projet, à terme, sera de définir une intelligence artificielle différente pour chaque ennemis. Penser à définir les bonnes coordonnées de départ pour chaque ennemi (voir les explications dans le code Scratch du lutin “ennemi“).
Ajouter l’arrière-plan “fond_bonus_gobtou.png“.
Partie 2 – Programmation
Le programme devra suivre le cheminement suivant :
Fixer le nombre de vies à 3 et les points à 0;
Répéter tant que le nombre de vie est supérieur à 0 et le nombre de points maximum n’est pas atteint;
Si une touche de direction est pressée, vérifier si le joueur peut emprunter cette direction et, le cas échéant, actualiser la direction du joueur dans le sens de la flèche pressée;
Faire avancer le joueur dans la direction demandée;
Faire avancer les ennemis;
Tester si un ennemi touche un joueur. Si c’est le cas, enlever une vie et repositionner les lutins en position de départ;
Tester si le joueur touche un bonus. Si c’est le cas, ajouter un point et effacer le bonus de l’écran.
Pseudo code pour le lutin joueur
Vie←0 Points←0 Direction←0 TantQue Vie!=0 || Points!=nombre de bonus Si touche "flèche haut" pressee==vrai Alors Si lutin touche mur==faux Alors Direction←haut Fin Si Fin Si Si touche "flèche bas" pressee==vrai Alors Si lutin touche mur==faux Alors Direction←bas Fin Si Fin Si Si touche "flèche droite" pressee==vrai Alors Si lutin touche mur==faux Alors Direction←droite Fin Si Fin Si Si touche "flèche gauche" pressee==vrai Alors Si lutin touche mur==faux Alors Direction←gauche Fin Si Fin Si Faire avancer le joueur vers Direction Si bonus touché==vrai Alors Points←Points+1 Effacer le bonus Fin Si Fin TantQue
Explication du pseudo code
Nous initialisons les variables Vie, Points et Direction à 0.
Le reste du code s’exécutera tant qu’il reste des vies au joueur ou tant qu’il reste des bonus à l’écran.
Nous testons si la flèche vers le haut est pressée. Lorsqu’elle est pressée, nous testons si le lutin peut monter. Si c’est le cas, nous fixons la direction du mouvement vers le haut.
Nous effectuons les mêmes test pour les flèches du bas, de droite et de gauche.
Nous faisons avancer le joueur dans la direction fixée par la variable Direction.
Si le lutin touche un bonus, nous incrémentons la variable Points de 1 et effaçons le bonus.
Pseudo code pour le lutin ennemi
Direction←nombre aléatoire entre 1 et 4 Boucle Infinie Si ennemi touche joueur==vrai Alors Vie←Vie-1 Afficher tous les lutins à leurs positions d'origine Fin Si Si nombre de directions possibles>2 Alors Direction←nombre aléatoire entre 1 et 4 Fin Si Si mur touché==vrai Alors Direction←nombre aléatoire entre 1 et 4 Sinon Faire avancer le lutin vers Direction Fin Si Fin Boucle Infinie
Explication du pseudo code
Nous affectons un nombre aléatoire entre1 et 4 à la variable Direction. (Par convention, nous fixons 1 : droite, 2 : haut, 3 : gauche et 4 : bas.)
Le reste du code sera effectué pendant toute la partie.
Si un ennemi touche le joueur, nous décrémentons la variable Vie de 1 et nous replaçons tous les lutins à leurs positions de départ.
Si le lutin est à une intersection, nous lui affectons une directions aléatoire.
Si le lutin touche un mur, nous lui affectons une directions aléatoire, sinon, il continue d’avancer.
Programmation dans Scratch
Définissons les variables dont nous aurons besoin.
Vie : nombre de vie du joueur. Il faut cocher la case devant le nom de la variable pour qu’elle apparaisse à l’écran.
Points : nombre de point du joueur. Il faut cocher la case devant le nom de la variable pour qu’elle apparaisse à l’écran.
DirJoueur : direction vers laquelle se dirige le joueur.
Avance : vitesse du joueur. Cette variable est exprimée en pixel, elle représente le nombre de pixels qui est incrémenté à chaque fois que le lutin avance.
ProcDir : représente la prochaine direction que le joueur souhaite emprunter.
Code Scratch pour le lutin joueur
Nous commençons par affecter les bonnes valeurs aux variables, mettre le costume joueurdroite et positionner le joueur au centre du labyrinthe.
Nous créons une boucle dans laquelle nous insérerons le code qui gère le déplacement du joueur. Les conditions pour sortir de la boucles sont :
le nombre de vie est égal à 0;
le nombre de points est égal à 106. A chaque fois que le joueur mange un bonus, il marque 1 point. Le labyrinthe proposé contient 106 bonus. Il faudra penser à ajuster ce nombre si nous créons d’autres labyrinthes.
Nous allons maintenant tester les entrées clavier pour savoir si une des touches direction a été pressée. Nous avons convenu que lorsque le joueur avance dans une direction, il continue d’avancer, même s’il lâche la touche de direction correspondante. Lorsque le joueur presse une touche, c’est qu’il veut changer de direction, nous affectons donc la variable ProcDir. Nous lui donnons la valeur correspondant à la touche pressée et si aucune touche n’est pressée elle est égale à 0. Nous verrons plus loin comment nous traitons cette variable pour gérer le déplacement du joueur.
Maintenant, nous allons déplacer le lutin “joueur” dans le labyrinthe. Comme dans le projet labyrinthe, nous déplaçons notre lutin de Avance pixels, nous testons s’il touche un mur et si c’est le cas, nous le faisons reculer de la même distance. Nous procédons ainsi pour la droite et la gauche.
Lien
Lien vers le projet Labyrinthe : cliquer
Pour les déplacements verticaux, nous allons ajouter une condition. En effet, au milieu du labyrinthe, nous avons un tunnel qui nous permet de passer directement du haut vers le bas ou inversement. Lorsque le personnage se trouve contre les murs supérieurs du labyrinthe son ordonnée est égale à y=135, il ne peut pas aller plus haut. Si son ordonnée est supérieure à 135, c’est qu’il se trouve dans le tunnel donc nous le faisons passer directement en bas en affectant la valeur -135 à y.
Nous procédons de la même manière pour le bas du tunnel.
Lorsque le déplacement est fait, nous testons si le lutin touche un bonus. Si c’est le cas, nous incrémentons la variable Points de 1. Nous devons aussi effacer ce bonus, afin qu’il ne soit plus présent à l’écran. Nous allons utiliser le stylo en lui donnant la couleur blanche pour peindre l’arrière-plan et ainsi le faire disparaître. Au moment du contact, tout le bonus ne sera pas recouvert par le lutin “joueur“, il faut prendre une taille de crayon plus grande que le lutin, nous la fixons à 30. Seul le bonus sera effacé, car les murs ne font pas partie de l’arrière plan.
Il ne faut pas oublier d’insérer le code que nous venons de développer dans la boucle que nous avons créée au début.
Maintenant, nous allons traiter les changements de direction. Comme nous l’avons vu précédemment, lorsqu’une touche est pressée, c’est que le joueur veut changer de direction, nous affectons donc la variable ProcDir avec la valeur correspondant à la touche. Pour savoir si nous pouvons affecter cette valeur à la variable DirJoueur, nous regardons si le déplacement dans la direction demandée est possible. Le test effectué sera le même que pour les déplacements, nous avançons, nous regardons si le lutin touche le mur et nous le faisons reculer si le mur est touché. Si le mur n’est pas touché, la direction demandée est possible , nous changeons donc la valeur de la variable DirJoueur. Cette dernière gardera la valeur donnée jusqu’à ce que le lutin touche un mur ou qu’une nouvelle direction demandée soit possible. Le joueur continuera donc d’avancer, même si la touche direction est relâchée. Ensuite, nous basculons sur le costume qui représente le lutin allant vers la droite.
Nous obtenons donc le code suivant pour le test sur la droite :
Pour les autres directions, nous avons :
Ces tests s’effectueront dès le début du jeu et pendant toute la durée de la partie, nous insérons donc le code que nous venons de développer dans une boucle infinie.
Lorsque le lutin “joueur” sera touché par un lutin “ennemi“, ce dernier enverra un message “touché”. Quand nous recevons ce message, nous annulons le déplacement du joueur, nous enlevons une vie et replaçons le lutin au centre du labyrinthe.
Lorsque la partie est terminée, nous écrivons “Partie terminée”” pendant deux secondes et nous arrêtons tous les scripts.
Code Scratch pour le lutin “ennemi”
Nous allons définir trois nouvelles variables qui ne seront utilisées que par ce lutin. Lorsque nous dupliquerons les lutins ennemis, ces variables deviendront des variables locales spécifiques à chaque lutin dupliqué.
DirEnnemi : direction de l’ennemi.
AvanceEnnemi : vitesse de l’ennemi.
PossibiliteEnnemi : donne le nombre de directions possibles dans une intersection.
Nous plaçons le lutin dans le coin inférieur droit, fixons sa vitesse de déplacement à 2 et sa direction est tirée aléatoirement. Nous n’effectuons pas de test pour savoir si la direction choisie est possible. Si le lutin ne peut pas aller dans cette direction, une nouvelle valeur sera tirée au tour suivant, jusqu’à avoir une bonne valeur. Les calculs sont effectués si rapidement, que nous ne verrons même pas le lutin hésiter s’il faut tirer plusieurs valeurs avant d’avoir une direction possible. Nous ferons de même dans la suite du programme, ceci nous permet d’alléger l’écriture.
Le reste du code devra être inséré dans une boucle infinie.
Nous allons déplacer le lutin. Nous ne pouvons pas utiliser les valeurs haut, bas ,droite et gauche, car les directions sont tirées au hasard, il sera plus simple, pour la suite, d’utiliser la convention suivante :
1 : droite;
2 : haut;
3 : gauche;
4 : bas.
Le mouvement est géré comme pour le joueur, en tenant compte du tunnel, sauf que si le lutin touche un mur, on tire une nouvelle direction au hasard. Nous avons donc, pour les quatre directions :
Une fois le lutin déplacé, nous testons s’il y a collision avec le joueur.
Maintenant, nous allons tester si l’ennemi se trouve à une intersection. Si c’est le cas, nous tirerons une nouvelle direction au hasard. D’abord, nous réinitialisons la variable PossibiliteEnnemi avec la valeur 0. Comme pour le joueur, nous avançons, testons si le lutin touche le mur et nous le faisons reculer. Si le mur n’est pas touché, nous incrémentons la variable PossibiliteEnnemi de 1.
Note
Le lutin ennemi se déplace de 2 pixels. Il peut arriver, s’il est mal centré dans le couloir, que le lutin avance de 2 pixels vers un mur sans le toucher (si le mur se trouve à 3 pixels de distance, par exemple). Pour tester les intersections, nous allons donc faire avancer le lutin de deux fois la valeur de la variable AvanceEnnemi, ainsi nous serons sûr des tests de collision avec les murs.
Pour tester l’existence d’un couloir sur la droite, nous avons donc le code suivant :
Nous répétons le même test pour les trois autres directions.
Si le nombre de possibilités est plus grand que 2, nous nous trouvons à une intersection, nous tirons donc une nouvelle direction aléatoirement.
Quand le joueur a été touché par l’un des lutins “ennemi“, le message “touché” est reçu. nous arrêtons le déplacement, nous introduisons une temporisation d’un demi seconde, plaçons l’ennemi à sa position de départ et tirons une valeur aléatoire pour le déplacement.
Quand le message “partie terminée” est reçu, on stoppe tous les scripts du lutin.
La programmation du lutin ennemi est maintenant terminée. Vous pouvez le dupliquer trois fois afin d’avoir quatre ennemis sur le tableau et de rendre la tâche du joueur plus difficile. Il faut bien penser à mettre les bonnes coordonnées pour les nouveaux lutin :
coin inférieur gauche : x=-186 y=-134
coin supérieur gauche: x= -186 y=134
coin supérieur droit : x=186 y=134
Les coordonnées doivent être modifiées dans la boucle principale, mais aussi dans le bloc d’instructions quand je reçois “mangé”.
Code Scratch pour le lutin “murs”
Il suffit juste de placer le lutin à la bonne position pour qu’il soit centré avec l’arrière-plan.
Code Scratch pour l’arrière-plan
Nous introduisons la commande effacer tout qui a pour effet d’effacer tout ce qui a été écrit avec le stylo pour faire apparaître tous les bonus en début de partie.
Pour aller plus loin
Nous pouvons ajouter un bonus spécial qui permettrait de manger ou de tirer sur les ennemis pendant un certain temps.
Nous pouvons continuer lorsque tous les bonus ont été mangé en recommençant le tableau ou en introduisant de nouveaux niveaux.
Nous pouvons implémenter une intelligence artificielle pour les lutins ennemi. Au lieu de se déplacer au hasard, leur mouvement aurait un but (se rapprocher du joueur, par exemple).