Attrap'œuf

Créations

JulienGio

il y a 5 ans

Les œuf: Attrapez-les tous!

Durée 50 minutes

Niveau Débutant

Prérequis

  • Avoir complété le Chapitre 1
  • L'atelier TapTap

Attrap'œuf est un mini-jeu assez simple. Le joueur utilise les flèches gauche et droite pour déplacer un panier. Il doit attraper le plus d'œufs possible, chaque œuf fait grandir le panier. Mais attention, il faut éviter les carottes! Les carottes détruisent les œufs dans le panier! Si jamais le panier atteint une taille de 0, la partie est perdue.

Cet atelier présente deux manières de réduire les lignes de code redondantes. Dans l'atelier précédent, vous avez peut-être remarqué que de nombreuses lignes de code ressemblaient énormément à d'autres lignes de code. Et bien souvent ces lignes étaient juxtaposées. Voici quelques extraits de l'atelier TapTap:

  // Mélanger les briques
  briques[0] = random(GAUCHE, DROITE + 1);
  briques[1] = random(GAUCHE, DROITE + 1);
  briques[2] = random(GAUCHE, DROITE + 1);
  briques[3] = random(GAUCHE, DROITE + 1);
  briques[4] = random(GAUCHE, DROITE + 1);
  // Décaler tout vers le bas


briques[0] = briques[1]; briques[1] = briques[2]; briques[2] = briques[3]; briques[3] = briques[4]; briques[4] = random(GAUCHE, DROITE + 1); // Nouvelle brique

  if (briques[1] == GAUCHE) {
    gb.display.drawRect(25, 30, 20, 10);
  } else {
    gb.display.drawRect(35, 30, 20, 10);
  }
  if (briques[2] == GAUCHE) {
    gb.display.drawRect(25, 20, 20, 10);
  } else {
    gb.display.drawRect(35, 20, 20, 10);
  }
  if (briques[3] == GAUCHE) {
    gb.display.drawRect(25, 10, 20, 10);
  } else {
    gb.display.drawRect(35, 10, 20, 10);
  }
  if (briques[4] == GAUCHE) {
    gb.display.drawRect(25, 0, 20, 10);
  } else {
    gb.display.drawRect(35, 0, 20, 10);
  }


Notre objectif ici sera de prendre ces blocs de code et de les réduire en taille, et aussi de rendre notre code bien plus flexible. Dans l'atelier précédent, nous n'avions que cinq briques, mais dites-vous que si vous en aviez 300, ça fait 300 copié-collés! Et maintenant imaginez que vous changez d'avis sur un détail (comme afficher des cercles à la place des rectangles)... C'est 300 modifications à faire à la main! L'horreur!

Pour programmer Attrap'œuf, nous allons voir les fonctions et la boucle for. Ce sont des notions qui s'intègrent très bien avec ce que nous avons vu précédemment: le format "entrées / mise à jour / affichage" et les tableaux.

Panier à œufs

Comme d'habitude, avant de remplir notre IDE de lignes de code. Prenons une petite pause pour placer nos différentes parties. Nous allons appliquer l'entrée / mise à jour / affichage.

<Gamebuino-Meta.h>

void setup() {
    gb.begin();
}

void loop() {
    while(!gb.update());
    // ENTREES
    // MISE A JOUR
    // AFFICHAGE
}


Commençons par programmer le panier. Le panier est très similaire aux raquettes que nous avons vu lors du Pong, sauf que maintenant, le panier est horizontal.

#include <Gamebuino-Meta.h>

// Panier
int panierX = 20;  // Le panier peut bouger horizontalement, donc ce n'est pas une constante
const int PANIER_Y = gb.display.height() - 4;  // Constante car le panier est fixe selon l'axe Y
int panierLargeur = 0;  // Pas cosntant car la taille dépend de la nourriture restante
const int PANIER_HAUTEUR = 3;  // Constante

int score = 0;
int highscore = 0;
int nourriture;  // Plus de nourriture est mieux. 0 nourriture = game over


void setup() {
  gb.begin();

  nourriture = 300;
}

void loop() {
    while(!gb.update());
    // ENTREES
    if (gb.buttons.repeat(BUTTON_LEFT, 0) && panierX > 0) {
        panierX -= 2;
    }
    else if (gb.buttons.repeat(BUTTON_RIGHT, 0) && panierX < gb.display.width() - panierLargeur) {
        panierX += 2;
    }

    // MISE A JOUR
    // La nourriture fait doucement baisser notre nourriture dans la panier
    nourriture--;
    panierLargeur = nourriture / 20;

    // Somme nous mort par manque de nourriture??
    if (nourriture <= 0) {
        score = 0;  // Recommencer le jeu
        nourriture = 300;  // Reset nourriture
    }

    // AFFICHAGE
    gb.display.clear();

    // Panier
    gb.display.setColor(WHITE);
    gb.display.fillRect(panierX, PANIER_Y, panierLargeur, PANIER_HAUTEUR);

    // Score
    gb.display.setColor(WHITE);
    gb.display.setCursor(gb.display.width() - 8, 0);
    gb.display.print(score);

    gb.display.setColor(RED);
    gb.display.setCursor(gb.display.width() - 8, 8);
    gb.display.print(highscore);
}

Le joueur peut déplacer le panier avec les flèches gauche et droite. Mais on l'empêche de sortir de l'écran. J'en ai aussi profité pour commencer à implémenter le système de nourriture. Comme nous avons dit précédemment, le joueur doit remplir son panier d'œufs. Chaque œuf augmente sa quantité de nourriture. Mais à chaque image de jeu, cette quantité diminue un peu. Il y a aussi les carottes qui diminue énormément la valeur de nourriture. Pour pouvoir visualiser nourriture, la largeur du panier est directement liée à sa valeur. Si jamais elle atteint 0, la partie est perdu.

Fonctions

Avant de commencer à implémenter les œufs et les carottes, regardons ensemble ce qu'est une fonction. Les fonctions sont un aspect très important du C++, elles vous permettent de découper votre code en blocs réutilisables. Les fonctions sont très pratiques car elles nous laissent repartir notre code sur plusieurs fichiers (nous y viendrons dans un instant) et de s'organiser. setup() et loop() sont des fonctions par exemple. Vous avez créé ces fonctions et Arduino les appelle automatiquement. Mais vous pouvez créer et appeler vos propres fonctions.

Ce que nous allons faire, c'est simplement d'extraire nos trois parties (entrées / mise à jour / affichage) en trois fonctions. Comme ça notre boucle loop() ressemblera à ça:

void loop() {
    while(!gb.update());
    entrees();
    miseAJour();
    affichage();
}

En quoi c'est utile? Bonne remarque, en fin de compte, avec ou sans fonctions, cela revient à la même chose non? Et c'est bien vrai, à chaque fois que vous appelez une fonction, cela revient exactement à copier-coller le code de la fonction là où vous l'avez appelé. Mais le but des fonctions est d'organiser le code (vous avez peut-être remarqué que ce chapitre se focalise beaucoup la-dessus ????). Les fonctions permettent aussi de d'exécuter la même suite d'instructions à plusieurs endroits dans le programme. En réalité, vous utilisez des fonctions depuis tout premier atelier! Toutes les fonctions dans la référence sont... et bien, des fonctions. Notamment:

Si à chaque fois que vous avez utilisé une de ces fonctions vous aviez taper tout le code qui se cache derrière... Vous en serait encore au début du premier chapitre! En plus, si nous trouvons un bug dans une des fonctions, il suffit juste que nous corrigeons la fonction, vous n'avez pas besoins d'intervenir. Bref, les fonctions sont nos amies. Mais comment en créer?


type nomDeFonction() {
    // Code de la fonction ici
}

Pour définir une fonction, il suffit de faire comme ci-dessus. Là où vous trouvez type, on doit y mettre le type de retour de la fonction. En effet les fonctions peuvent "retourner" une valeur avec le mot clé return. Par exemple:

// Ici on définit la fonction //
int aireDuPanier() {
    return panierLargeur * PANIER_HAUTEUR;  // retourner l'aire du panier
}

// Autre part dans notre code //
    int aire = aireDuPanier();

Attention, le mot return termine la fonction. C'est-à-dire que tout ce que vous mettez après return ne sera même pas exécuté. Mais maintenant, vous allez me dire: Mais Julien, les fonctions setup() et loop() ont void comme type de retour, qu'est-ce que void? C'est vrai que void n'est pas un type. C'est justement l'absence de type. Dire qu'une fonction retourne void signifie qu'elle ne retourne rien. Comme vous pouvez le voir, setup() et loop() n'utilisent jamais le mot return.


Nous allons mettre nos trois fonctions dans trois autres fichiers. C'est mieux pour nous d'avoir plusieurs fichiers de taille modéré, qu'un seul fichier gigantesque. De cette manière, nous pourrons retrouver notre code bien plus rapidement. Pour créer un fichier, il suffit de cliquer sur la petite flèche en haut à droite de l'IDE, puis sur "Nouvel onglet"

Il faut savoir que Arduino lira tous les fichiers dans un certain ordre. Mais il commencera toujours par le fichier ayant les fonctions setup() et loop() ("Attrapoeuf.ino" dans mon cas). Il faut donc déclarer toutes les variables globales dans ce fichier-là, comme ça, toutes les fonctions y auront accès.

Donc notre programme devient le suivant.

// Attrapoeuf.ino //
#include <Gamebuino-Meta.h>

// Panier
int panierX = 20;  // Le panier peut bouger horizontalement, donc ce n'est pas une constante
const int PANIER_Y = gb.display.height() - 4;  // Constante car le panier est fixe selon l'axe Y
int panierLargeur = 0;  // Pas constant car la taille dépend de la nourriture restante
const int PANIER_HAUTEUR = 3;  // Constante

int score = 0;
int highscore = 0;
int nourriture;  // Plus de nourriture est mieux. 0 nourriture = game over


void setup() {
  gb.begin();

  nourriture = 300;
}

void loop() {
    while(!gb.update());
    entrees();
    miseAJour();
    affichage();
}
// entrees.ino //


void entrees() { if (gb.buttons.repeat(BUTTON_LEFT, 0) && panierX > 0) { panierX -= 2; } else if (gb.buttons.repeat(BUTTON_RIGHT, 0) && panierX < gb.display.width() - panierLargeur) { panierX += 2; } }

// miseAJour.ino //
void miseAJour() {
    // La nourriture fait doucement baisser notre nourriture dans la panier
    nourriture--;
    panierLargeur = nourriture / 20;


// Somme nous mort par manque de nourriture?? if (nourriture <= 0) { score = 0; // Recommencer le jeu nourriture = 300; // Reset nourriture } }

// affichage.ino //
void affichage() {
    gb.display.clear();


// Panier gb.display.setColor(WHITE); gb.display.fillRect(panierX, PANIER_Y, panierLargeur, PANIER_HAUTEUR);

// Score gb.display.setColor(WHITE); gb.display.setCursor(gb.display.width() - 8, 0); gb.display.print(score);

gb.display.setColor(RED); gb.display.setCursor(gb.display.width() - 8, 8); gb.display.print(highscore); }

Les Boucles for


Il nous reste à programmer la "pluie" d'œufs et de carottes. Nous commencerons avec les œufs. Du point de vue du joueur, on dirait qu'une infinité d'œufs tombent. Mais programmer une infinité d'objets, avec une quantité limitée de mémoire, ne va pas être possible malheureusement. Nous allons procéder plus simplement. Il y aura une dizaine d'œufs qui chuterons depuis une hauteur aléatoire. Une fois qu'un œuf atteint le bas de l'écran (ou s'il touche le panier), on le replace en haut. Ceci donnera l'illusion d'avoir une infinité d'œufs, et a pour seule limite d'avoir un maximum de 10 œufs sur l'écran à un moment donné.

Pour caractériser un œuf, nous avons besoins de quatre éléments: sa position horizontale, sa position verticale, sa largeur et sa hauteur. La hauteur et la largeur des œufs peuvent être mémorisé avec des constantes. La position, par contre, varie d'œuf en œuf. Il nous faudra donc des tableaux. Nous allons avoir deux tableaux, un pour les positions selon X, et un pour les positions selon Y.

Chaque indice correspond aux coordonnées d'un œuf. Donc le 3ème œuf a comme position (oeufsX[2], oeufsY[2]) (Petit rappel: les indices de tableaux commencent avec 0).


// Attrapoeuf.ino //
// ...

const int NBR_D_OEUFS = 10;
int oeufsX[NBR_D_OEUFS] = {};
int oeufsY[NBR_D_OEUFS] = {};

const int OEUF_LARGEUR = 2;
const int OEUF_HAUTEUR = 3;

void setup() {
    gb.begin();
    nourriture = 300;
    // Placer les oeufs aléatoirement au dessus de l'écran
    oeufsX[0] = random(0, gb.display.width());  // 1er oeuf
    oeufsY[0] = random(-40, -OEUF_HAUTEUR);
    oeufsX[1] = random(0, gb.display.width());  // 2eme oeuf
    oeufsY[1] = random(-40, -OEUF_HAUTEUR);
    oeufsX[2] = random(0, gb.display.width());  // 3eme oeuf
    oeufsY[2] = random(-40, -OEUF_HAUTEUR);
    oeufsX[3] = random(0, gb.display.width());  // 4eme oeuf
    oeufsY[3] = random(-40, -OEUF_HAUTEUR);
    oeufsX[4] = random(0, gb.display.width());  // 5eme oeuf
    oeufsY[4] = random(-40, -OEUF_HAUTEUR);
    oeufsX[5] = random(0, gb.display.width());  // 6eme oeuf
    oeufsY[5] = random(-40, -OEUF_HAUTEUR);
    oeufsX[6] = random(0, gb.display.width());  // 7eme oeuf
    oeufsY[6] = random(-40, -OEUF_HAUTEUR);
    oeufsX[7] = random(0, gb.display.width());  // 8eme oeuf
    oeufsY[7] = random(-40, -OEUF_HAUTEUR);
    oeufsX[8] = random(0, gb.display.width());  // 9eme oeuf
    oeufsY[8] = random(-40, -OEUF_HAUTEUR);
    oeufsX[9] = random(0, gb.display.width());  // 10eme oeuf
    oeufsY[9] = random(-40, -OEUF_HAUTEUR);
}

void loop() {
    // ...
}

Ici, je me suis arrêté à la fin de setup(). Mais regardons ces lignes. Il y a juste les déclarations en début de programme, et une vingtaines de lignes pour placer les œufs aléatoirement au-dessus de l'écran avec random(-40, -OEUF_HAUTEUR);. En les plaçant avec une position Y négative, les œufs vont initialement être hors de l'écran, puis on les fera descendre dans la zone d'affichage.

Mais si on regarde la forme des ces lignes, on voit plusieurs lignes côte à côte qui se ressemble énormément. C'est exactement le type de code que j'ai dit que nous allions éviter au tout début de l'atelier. Et c'est ici que je vous dévoile les boucles for.

Les boucles sont utilisées en programmation pour exécuter le même bloc de code plusieurs fois à la suite. Il existe trois types de boucles en C++:

  • les boucles for (celle que nous étudierons)
  • les boucles while
  • les boucles do-while


for (initialisation; condition; incrémentation) {
    // Code...
}

Les boucles for ont quatre parties. Dans initialisation on déclare une variable qui nous servira de compteur. Ensuite, on place une condition, le même style de condition qu'avec les if. L'incrémentation correspond à une instruction qui fait varier la valeur de notre compteur, généralement c'est compteur += 1. Et enfin, on place le code entre les accolades { }.

Ce sont bien des points-virgules ; entre l'initialisation, la condition et l'incrémentation.



Quand on entre dans la boucle, l'initialisation est exécutée. Ensuite, après avoir traversé le code entre accolades, l'incrémentation est exécutée, puis la condition est testée. Si elle est toujours vraie, on recommence du haut de la boucle. Prenons un exemple: la somme des nombres de 1 à 10:

int somme = 0;
for (int i = 1; i <= 10; i += 1) {
    somme += i;
}
gb.display.print(somme);

Si la condition n'est pas vraie avant d'entrer dans la boucle, son code sera complètement sauté.


Lorsqu'on entre dans la boucle pour la première fois, on ajoute 1 à somme. i deviens 2 et 2 est bien inférieur à 10 donc on ajoute 2 à notre somme. Puis 3. Etc jusqu'à ce que i soit égale à 10. Là on ajoute 10 et puis on incrémente. Donc i vaut maintenant 11. Or 11 n'est pas inférieur ou égale à 10. Donc on sort de la boucle et on affiche la somme! Tout ça en cinq lignes :)

En faisant incrémenter une variable, on peut parcourir tous les éléments d'un tableau. C'est ici que les tableaux montrent leurs plus grande utilité. Prenons directement notre code dans setup() et réécrivons le avec une boucle :D

// Attrapoeuf.ino //
// ...

void setup() {
    gb.begin();
    nourriture = 300;
    // Placer les oeufs aléatoirement au dessus de l'écran
    for (int i = 0; i < NBR_D_OEUFS; i += 1) {
        oeufsX[i] = random(0, gb.display.width());
        oeufsY[i] = random(-40, -OEUF_HAUTEUR);  // Au dessus de l'écran
    }    
}

//...

Bien plus simple non? Avoir structuré notre code avec des tableaux et des boucles for nous permet de faire quelque chose de génial. Vous voulez 20 œufs sur l'écran? const int NBR_OEUF = 20; Vous en voulez encore plus peut-être? const int NBR_OEUF = 500;. Vous n'avez plus qu'a changer une seule ligne de code si vous voulez changer la quantité d'œufs!



// miseAJour.ino //
void miseAJour() {
    // Mise a jour de la nourriture et de la taille //
    // Vérifier que le panier ne sois pas vide //

    // Oeufs
    for (int i = 0; i < NBR_D_OEUFS; i += 1) {
        oeufsY[i] += 1;  // Déplacer l'oeuf vers le bas
        // Collision avec le panier
        if (gb.collide.rectRect(panierX, PANIER_Y, panierLargeur, PANIER_HAUTEUR, 
            oeufsX[i], oeufsY[i], OEUF_LARGEUR, OEUF_HAUTEUR)) {
            score += 1;
            if (score > highscore) {
                highscore = score;
            }
            nourriture = nourriture + 20;  // MMmmmmm des oeufs

            // Reset l'oeuf
            oeufsX[i] = random(0, gb.display.width());
            oeufsY[i] = random(-40, -OEUF_HAUTEUR);  // Au dessus de l'écran
        }

        // Sortie de l'écran
        if (oeufsY[i] >= gb.display.height()) {
            // Reset l'oeuf
            oeufsX[i] = random(0, gb.display.width());
            oeufsY[i] = random(-40, -OEUF_HAUTEUR);  // Au dessus de l'écran
        }
    }
}

Ici j'ai aouté la logique de jeu. On a une boucle qui, pour chaque œuf, regarde s'il est entré en collision avec le panier, puis s'il est sorti de l'écran. Précédemment, pour évaluer une collision, nous avons calculer par nous-même à l'aide d'un schéma, notamment lors du Pong. Mais ce que vous ne saviez pas, c'est qu'il existe une fonction toute prête qui le fait pour vous :D

gb.collision.rectRect(rect1X, rect1Y, rect1Largeur, rect1Hauteur, rect2X, rect2Y, rect2Largeur, rect2Hauteur)

On passe les positions et dimensions des deux rectangles, et la fonction retourne vrai s'ils se chevauchent.



// affichage.ino //
void affichage() {
    // Afficher le panier //

    // Oeufs
    gb.display.setColor(BROWN);
    for (int i = 0; i < NBR_D_OEUFS; i += 1) {
        gb.display.fillRect(oeufsX[i], oeufsY[i], OEUF_LARGEUR, OEUF_HAUTEUR);
    }

    // Afficher le score et highscore //
}

Ici, on utilise une boucle pour afficher tous les œufs.

A vous de jouer!

Notre jeu Attrap'oeuf n'est pas encore fini. Ensemble, nous avons ajouté le panier et les œufs. Il vous reste à implémenter les carottes. Mais avec l'aide des boucles for et l'organisation des fonctions, je suis sûr que vous pouvez le faire! Petites indications de design: commencer par ajouter un nombre fixe de carottes. Ensuite essayez de créer un mini système de progression, c'est-à-dire que plus le score est grand, plus il y a de carottes qui tombent, avec un quantité maximal de carottes de na pas dépasser. Autre mission, centrez le panier pour qu'il change de taille sûr la gauche et la droite (actuellement, seul le côté droit grandit/rapetisse).

  • Astuce #1: Par rapport au système de progression, vous pouvez utiliser la fonction min() pour ne pas dépasser le nombre maximal de carottes (en savoir plus ici).
  • Astuce #2: Toujours pour le système de progression, il vous faudra une variable qui traque le nombre de carottes actuellement en jeu, et une constante pour le nombre maximal de carottes.
  • Astuce #3: Pour centrer le panier, il faut que panierX corresponde au centre du panier. A vous de trouver comment l'afficher et gérer les collisions.

Partage ton talent sur les réseaux avec #gamebuino #atelier #attrapoeuf, on les regarde tous ;)

Exemple de Solution

Si vous êtes en panne d'inspiration, voilà ce qu'on a fait de notre côté :)


Attrapoeuf.ino

#include <Gamebuino-Meta.h>

// Eggs
const int NBR_D_OEUFS = 10;
int oeufsX[NBR_D_OEUFS] = {};
int oeufsY[NBR_D_OEUFS] = {};

const int OEUF_LARGEUR = 2;
const int OEUF_HAUTEUR = 3;

// Carots
const int NBR_MAX_DE_CAROTTES = 8;
int nbrDeCarottes = 0;
int carottesX[NBR_MAX_DE_CAROTTES] = {};
int carottesY[NBR_MAX_DE_CAROTTES] = {};

const int CAROTTE_LARGEUR = 3;
const int CAROTTE_HAUTEUR = 5;

// Panier
int panierX = 20;  // Le panier peut bouger horizontalement, donc ce n'est pas une constante
const int PANIER_Y = gb.display.height() - 4;  // Constante car le panier est fixe selon l'axe Y
int panierLargeur = 0;  // Pas constant car la taille dépend de la nourriture restante
const int PANIER_HAUTEUR = 3;  // Constante

int score = 0;
int highscore = 0;
int nourriture;  // Plus de nourriture est mieux. 0 nourriture = game over

void setup() {
  gb.begin();
  // Placer les oeufs aléatoirement au dessus de l'écran
  for (int i = 0; i < NBR_D_OEUFS; i += 1) {
    oeufsX[i] = random(0, gb.display.width());
    oeufsY[i] = random(-40, -OEUF_HAUTEUR);  // Au dessus de l'écran
  }
  nourriture = 300;
}

void loop() {
  while (!gb.update());
  entrees();
  miseAJour();
  affichage();
}


entrees.ino

void entrees() {
  if (gb.buttons.repeat(BUTTON_LEFT, 0) && panierX > 0) {
    panierX -= 2;
  }
  else if (gb.buttons.repeat(BUTTON_RIGHT, 0) && panierX < gb.display.width()) {
    panierX += 2;
  }
}


miseAJour.ino

void miseAJour() {
  // La faim fait doucement baisser notre nourriture dans la panier
  nourriture--;
  panierLargeur = nourriture / 20;
  if (nourriture <= 0) {  // Somme nous mort de faim??
    nbrDeCarottes = 0;  // Reset les carottes
    score = 0;  // Recommencer le jeu
    nourriture = 300;  // Reset nourriture
    for (int i = 0; i < NBR_D_OEUFS; i += 1) {
      oeufsX[i] = random(0, gb.display.width());
      oeufsY[i] = random(-40, -OEUF_HAUTEUR);  // Au dessus de l'écran
    }
  }

  // Eggs
  for (int i = 0; i < NBR_D_OEUFS; i += 1) {
    oeufsY[i] += 1;

    // Collisions
    if (gb.collide.rectRect(panierX - panierLargeur / 2, PANIER_Y, panierLargeur, PANIER_HAUTEUR, oeufsX[i], oeufsY[i], OEUF_LARGEUR, OEUF_HAUTEUR)) {
      score += 1;
      if (score > highscore) {
        highscore = score;
      }
      nourriture = nourriture + 20;  // MMmmmmm des oeufs
      // Ajouter des carrotes. Sans dépasser le plafond
      nbrDeCarottes = min(score / 5, NBR_MAX_DE_CAROTTES);

      // Reset l'oeuf
      oeufsX[i] = random(0, gb.display.width());
      oeufsY[i] = random(-40, -OEUF_HAUTEUR);  // Au dessus de l'écran
    }

    // Verifier que la carotte ne soit pas sortie de l'écran
    if (oeufsY[i] >= gb.display.height()) {
      // Reset l'oeuf
      oeufsX[i] = random(0, gb.display.width());
      oeufsY[i] = random(-40, -OEUF_HAUTEUR);  // Au dessus de l'écran
    }
  }

  // Carottes
  for (int i = 0; i < nbrDeCarottes; i += 1) {
    carottesY[i] += 2;

    // Collisions avec le joueur
    if (gb.collide.rectRect(panierX - panierLargeur / 2, PANIER_Y, panierLargeur, PANIER_HAUTEUR, carottesX[i], carottesY[i], CAROTTE_LARGEUR, CAROTTE_HAUTEUR)) {

      nourriture -= 40;  // Allergie => moins de nourriture :(

      // Reset la carotte
      carottesX[i] = random(0, gb.display.width());
      carottesY[i] = random(-20, -CAROTTE_HAUTEUR);  // Au dessus de l'écran
    }

    // Verifier que la carotte ne soit pas sortie de l'écran
    if (carottesY[i] >= gb.display.height()) {
      // Reset la carotte
      carottesX[i] = random(0, gb.display.width());
      carottesY[i] = random(-20, CAROTTE_HAUTEUR);  // Au dessus de l'écran
    }
  }

}


affichage.ino

void affichage() {
  gb.display.clear();

  // Eggs
  gb.display.setColor(BROWN);
  for (int i = 0; i < NBR_D_OEUFS; i += 1) {
    gb.display.fillRect(oeufsX[i], oeufsY[i], OEUF_LARGEUR, OEUF_HAUTEUR);
  }

  // Carrots
  gb.display.setColor(ORANGE);
  for (int i = 0; i < nbrDeCarottes; i += 1) {
    gb.display.fillRect(carottesX[i], carottesY[i], CAROTTE_LARGEUR, CAROTTE_HAUTEUR);
  }

  // Player
  gb.display.setColor(WHITE);
  gb.display.fillRect(panierX - panierLargeur / 2, PANIER_Y, panierLargeur, PANIER_HAUTEUR);

  // Score
  gb.display.setColor(WHITE);
  gb.display.setCursor(gb.display.width() - 8, 0);
  gb.display.print(score);
  gb.display.setColor(RED);
  gb.display.setCursor(gb.display.width() - 8, 8);
  gb.display.print(highscore);
}


Atelier Suivant

Par Julien Giovinazzo

Des questions / commentaires / suggestions? Laissez-nous un commentaire plus bas!

Voir la création

jicehel

NEW il y a 5 ans

Merci Julien  ;)  (Par contre dans le titre mets le S à Oeuf  ;) )

Juice_Lizard

NEW il y a 5 ans

J'ai enfin suivi tous les ateliers. Celui-là était cool aussi. Celui-là aussi a des petites erreurs à corriger: il manque les <Gamebuino-Meta.h> après les #include; "setup" ne prend toujours pas de "u" majuscule; il y a un "misaAJour" avec un "a" à la place du "e"; il manque des mots après "les œufs vont initialement être hors de l'écran, puis on les fera..." et enfin il semble que c'est le côté droit et non gauche du panier qui grandit et rapetisse (et non "rapetissit"). Maintenant, à moi de faire mon jeu et vous me direz si je fais des erreurs :-P

Aurélien Rodot

il y a 5 ans

Merci pour les correctifs, je m'occupe de ça ! Bon courage pour ton jeu, n'hésite pas à faire une Création pour partager ta progression et demander de l'aide si besoin :)

PS: Tu as fait le casse brique avant celui-ci, c'était pas trop compliqué dans le désordre ? C'était voulu ?

PPS: #include <Gamebuino-meta.h> n'est requis qu'une fois en début de programme, et uniquement sur ton .ino principal. Je viens de vérifier, il apparaît bien là où il devrait ????

PPPS: Tout le reste est corrigé :)

Aurélien Rodot

NEW il y a 5 ans

Juice_Lizard Juice_Lizard

Merci pour les correctifs, je m'occupe de ça ! Bon courage pour ton jeu, n'hésite pas à faire une Création pour partager ta progression et demander de l'aide si besoin :)

PS: Tu as fait le casse brique avant celui-ci, c'était pas trop compliqué dans le désordre ? C'était voulu ?

PPS: #include <Gamebuino-meta.h> n'est requis qu'une fois en début de programme, et uniquement sur ton .ino principal. Je viens de vérifier, il apparaît bien là où il devrait ????

PPPS: Tout le reste est corrigé :)

Juice_Lizard

il y a 5 ans

De rien! Je pourrai conseiller ces tutos à mes élèves d'arts plastiques sans qu'ils galèrent sur des bugs.

J'ai fait le casse brique en premier parce que j'avais déjà toutes les bases et que je voulais aller vite. Mais après je me suis dit que suivre tous les ateliers serait une bonne révision. Je connaissais les trois-quarts des concepts mais ça m'a bien mis dans le bain de la Gamebuino Meta, après avoir codé sur Arduboy. Et puis je souhaite prendre de bonne habitudes de code (par exemple: structurer avec inputs(), updates() et outputs()). J'essayerai d'obtenir le label qualité.

Il me semble qu'il n'y avait écrit que #include, sans <Gamebuino-Meta.h> derrière. Peut-être que quelqu'un a corrigé entre temps?

Maintenant j'ai besoin de savoir comment coder et afficher des sprites. C'est peut-être déjà prévu dans un futur atelier?

Juice_Lizard

NEW il y a 5 ans

Aurélien Rodot Aurélien Rodot

De rien! Je pourrai conseiller ces tutos à mes élèves d'arts plastiques sans qu'ils galèrent sur des bugs.

J'ai fait le casse brique en premier parce que j'avais déjà toutes les bases et que je voulais aller vite. Mais après je me suis dit que suivre tous les ateliers serait une bonne révision. Je connaissais les trois-quarts des concepts mais ça m'a bien mis dans le bain de la Gamebuino Meta, après avoir codé sur Arduboy. Et puis je souhaite prendre de bonne habitudes de code (par exemple: structurer avec inputs(), updates() et outputs()). J'essayerai d'obtenir le label qualité.

Il me semble qu'il n'y avait écrit que #include, sans <Gamebuino-Meta.h> derrière. Peut-être que quelqu'un a corrigé entre temps?

Maintenant j'ai besoin de savoir comment coder et afficher des sprites. C'est peut-être déjà prévu dans un futur atelier?

Aurélien Rodot

il y a 5 ans

Galérer sur les bugs, ça fait partie de l'apprentissage :P
Ah oui je l'ai déjà corrigé en effet :)

En effet on a prévu de continuer de faire des ateliers, on a déjà tout le programme de tracé, mais Julien qui les rédigeait n'est plus là :'(

Aurélien Rodot

NEW il y a 5 ans

Juice_Lizard Juice_Lizard

Galérer sur les bugs, ça fait partie de l'apprentissage :P
Ah oui je l'ai déjà corrigé en effet :)

En effet on a prévu de continuer de faire des ateliers, on a déjà tout le programme de tracé, mais Julien qui les rédigeait n'est plus là :'(

jicehel

NEW il y a 5 ans

Mince. Dommage que je déménage sur Rennes et non sur St Etienne à moins que le télé-travail soit possible ...  ;)  Bon non je plaisante mais sinon, tu peux faire un post avec la liste des tutos à rédiger et on peut essayer de s'y coller. Perso je n'ai pas les connaissances de certains, alors je peux essayer d'en écrire (et d'autres aussi) et d'autres peuvent intervenir (ça peut être un peu construit en collaboratif) pour corriger / améliorer l'article.

Après, je ne pense pas pouvoir en écrire sur tous les sujets faute de connaissance dans certains domaines, mais comme dit plus haut, je ne suis pas tout seul, je suis sûr qu'il y a plein de francophone et plein d'anglophone qui peuvent en écrire. Perso si j'en écris, je préfère le faire en français pour ne pas écorcher la langue de Shakespeare. Il faudrait donc aussi que des personnes se proposent en tant que rédacteurs / traducteurs.

D'ailleurs, je me disait un truc idiot comme je m'en dit souvent, mais ne serait-ce pas intéressant de voir s'il y a des volontaires pour traduire en d'autres langues. Tout ne peut être traduit, mais il faudrait que toutes les choses importantes soient traduites en Français/ Anglais / Espagnol / Allemand / ... en fonction des traducteurs (je pense qu'une langue ne peut être proposée que si l'on a au moins 3 traducteurs volontaires si l'on veut que la langue soit pérenne dans le temps).

Jacek Bogdan

NEW il y a 5 ans

Hi, when it will be availible in english?

jicehel

il y a 5 ans

When someone will make an english version  :)  If someone write english correctly and read french, he'll be able to propose a translation. Aurelien wrote that Julien who had to do it as gone, i don't know if someone else as join his team since and will do it. So to be sure, beter way is to propose a translation... I'm not good enough to do it correctly but i suppose than in the readers, there should be some good enough french to make an corect english translation.

jicehel

NEW il y a 5 ans

Jacek Bogdan Jacek Bogdan

When someone will make an english version  :)  If someone write english correctly and read french, he'll be able to propose a translation. Aurelien wrote that Julien who had to do it as gone, i don't know if someone else as join his team since and will do it. So to be sure, beter way is to propose a translation... I'm not good enough to do it correctly but i suppose than in the readers, there should be some good enough french to make an corect english translation.

jicehel

NEW il y a 5 ans

An idea as french people even if they talk english see only the french pages (and so don't even know that a page don't exist in english): On the Academy page, under the flag to see if you have complete a tuto or not, you could add a french flag / and or / an english flag to show the langage available. If english or french page don't exist (the page exist only when an admin as validate it), a button could be visible to propose a translation ?