Tally counter

Creations

Aurélien Rodot

6 years ago

Discover variables, the conditional structure if and draw rectangles.

Build a program to count guests, stock, or sheep

Length 20 minutes

Level Complete beginner

Prerequisites

Our objective now is to build a small guest counter, that increments whenever the use presses the UP arrow, and decrements whenever the Down arrow is pressed. This counter is often found at the entrance of stadiums or events to count people. But the logic we will use can be applied in many areas of a video game, such as scores or death count. Throughout the program, we will learn how to store values in memory with variables and use conditions to handle buttons.

Variables

Let's begin with the body of our last program, "Hello, world" and making a small variation: (don't hesitate to follow along on you IDE ;) )

#include 

int counter = 0;  // New line

void setup() {
  gb.begin();

}

void loop() {
  while (!gb.update());
  gb.display.clear();

  gb.display.print(counter);  // Something changed here :)
}

Let's take a deeper look at the changes we made.

int counter = 0;

This line declares and initializes a variable that we display later in loop

counter is a variable that keeps track of how many guests we have. A variable is used to store information in memory. It could be a score, an image, some text, or even a whole map for a platformer. Sadly, when you power off you console, all variables are lost :( Note for the curious: there is actually a way to store variables in the SD card as to not lose them! (we will learn more about this in an later workshop)

Here, we give our variable an initial value of 0 with the equals operator =.

A variable has a name, here we chose counter. Choosing a descriptive name is very important, our programs can be understood more clearly by the reader and ourselves. Avoid using names like a, b or var, unless you want headaches.

A variable also has a data type. The type of a variable is set at declaration, just before naming it. Here counter is an int. "Int" is short for integer, also known as whole numbers. So counter can store any integer, such as 0, 1, 5, or -34.

Here, this instruction is not placed inside of a function (functions are bounded by curly brackets {}). In C/C++, we can place instructions outside of functions, but these will only be executed once, from top to bottom. Note this does not apply to functions (instructions end in a semicolon but functions do not).


gb.display.print(counter);

We already saw how to use the gb.display.print function in the Hello world workshop. We used it with a string as its parameter. But you can also pass an int as a parameter, like we have done here. This will of course print the value of counter.

if - The Conditional Structure

If you run the code we wrote up to this point, you will see a screen displaying a 0. There is no movement, no interaction. Boring right? Let's fix this right now. Before we start coding, you must understand that a variable can change value, hence the name. Also it is good practice to write down an algorithm before we program. So I came up with this algorithm to create the guest counter:

If we press UP
Then add 1 to "counter"

If we press DOWN
Then subtract 1 to "counter"

If we press MENU
Then Reset "counter" to 0

This algorithm simply states that we control the value of counter with the UP and DOWN arrows. We can also reset its value with the MENU button. See nothing complicated right? Now we have to write the same thing, but in C++. The If ... Then ... in our algorithm translate to:

if (condition) {
  // ...
}

Rather straight forward. If the condition is true, Then execute everything located within those two curly brackets { }. Applying this to the first part of the algorithm (UP arrow), we get the following:

#include 

int counter = 0;

void setup() {
  gb.begin();

}

void loop() {
  while (!gb.update());
  gb.display.clear();

  if (gb.buttons.pressed(BUTTON_UP)) {  // If we press UP
    counter = counter + 1;    // Add 1 to "counter"
    gb.sound.playTick();    // Play a ticking sound
  }  // End of IF

  gb.display.print(counter);
  gb.display.fillRect(counter, 8, 2, 4);  // Draw the rectangle
}

The new instructions are explained below. Since we only give you one part of the algorithm, then...

It's your turn!

It is now up to you to complete the code we started so that the two other buttons DOWN and MENU work. Do not hesitate to read over the algorithm and help yourself with the code for the UP arrow. If you ever need help with syntax, go to the Reference page (I recommend that you bookmark that page). This time I'll be nice this time and provide most of the elements you will need:

BUTTON_DOWN
BUTTON_MENU

These are the names of the two other buttons you will need.

Once that works, here is an extra little challenge: personalize your counter. Here are the functions you can use (and that are used in the given code above):


gb.sound.playTick();

This plays an predefined "tick" sound. Could be useful to make the counter feel more responsive when the player presses a button.


gb.display.fillRect(x, y, w, h);

This function draws a rectangle. It takes multiple parameters, separated by comas ,. The number and order of parameters in a a function is important. If you want to read more about this function, check out its reference, but here is a quick summary:

  • x is the horizontal coordinate of the rectangle, with the left edge of the screen being 0. In the example above, ve used counter.
  • y is the vertical coordinate of the rectangle, with the top edge of the screen being 0. Here it is 8, or 8 pixels.
  • w is the width of the rectangle in pixels, here 2
  • h is the height of the rectangle in pixels, here 4

How you use theses functions is up to you! Go crazy! Try messing with the position and size of the rectangle, or how about it color? What if instead of counting guests 1 by 1, you multiplied by 2 every time.

Share your inventions on social networks with #gamebuino #tallycounter, we go through them all the time ;)


Remarks about C/C++

A computer (like your Gamebuino) can execute a huge number of instructions very quickly, but a computer is not very bright. It we do exactly as it is told, literally. So the syntax and logic of each line is rather strict. There are two types of errors: syntax errors and logic errors. Syntax errors are detected by the compiler (when you compile), who will not let you compile until it is happy. Then we have the logic errors, those get past the compiler, but things don't behave like they are supposed to when you run your game.

To fix syntax errors, reading the red text given by the compiler is crucial. Here are the most "popular" errors:

Curly brackets { }

This is an error that a lot of beginners make. It is important to know that curly brackets come in pairs. Sometimes, you may forget to put one or type an extra one by accident. Either way, the compiler will not be happy because there will be a lonely bracket without a partner somewhere :( Here is a great feature of this IDE: if you place your cursor on an opening bracket, the matching closing brackt will be highlighted (and vice-versa). Note: all if statements and functions must have brackets.

The semicolon ;


Every instruction must end with a semicolon. This error is as easy to fix, as it is to make :P Just read over your code, and look for missing semicolons.

If you ever get stuck with an error that you cannot seam to fix, come by our discord channel for help. Even if you don't have problems, you can join the community's chat ;)


Solution Example

Here's what we came up with: (code below)

#include 

 




int counter = 0;




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




void loop() {
  while (!gb.update());
  gb.display.clear();




  if(gb.buttons.pressed(BUTTON_UP)){
    counter = counter + 1;
    gb.sound.playOK();
  }




  if(gb.buttons.pressed(BUTTON_DOWN)){
    counter = counter - 1;
    gb.sound.playCancel();
  }




  if(gb.buttons.pressed(BUTTON_MENU)){
    counter = 0;
    gb.sound.playTick();
  }




  gb.display.setColor(BROWN);
  gb.display.fillRect(0, 0, counter, gb.display.height());




  gb.display.setColor(WHITE);
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter); 
}




 




Head to the next workshop to start you Pong game!

Next Workshop

By Aurélien Rodot, modified by Julien Giovinazzo

Any questions / comments / suggestions, be sure to drop a comment down below!

View full creation

Nux

NEW 6 years ago

Bien mieux que l'ancien. La nouvelle structure des Tuto avec les prérequis au début et une solution original et coloré à la fin est aussi bien pensé.

STUDIOCRAFTapps

NEW 6 years ago

Je crois que les type int sont en fait des uint64_t et non des int32_t comme il était sur le CPU de la Gamebuino Classic donc "On peut donc y stocker un nombre sans virgule, positif ou négatif." n'est pas vrai.

Basé selon mes experience personnel lorsque j'ai adapté mon jeu vers la META

Nan finalement les int ce n'est pas usigned

Lemmy

NEW 6 years ago

Salut tout le monde!  

J'aimerai creuser un peu l'idée du compteur et, par exemple, faire en sorte que le nombre à l'écran s'allume vert quand on appuie sur "haut" et rouge quand on appuie sur "bas"... je ne sais pas trop comment faire je me suis dit que ça serait facile mais en fait ça ne l'est pas (pour moi -_-).


#include <Gamebuino-Meta.h>

int counter = 0;

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

void loop() {
  while (!gb.update());
  gb.display.clear();

  if (gb.buttons.pressed(BUTTON_UP)) {
    counter = counter + 1;
    gb.display.setColor(GREEN);
    gb.display.print(counter);
    gb.sound.playOK();
    
  }

  if (gb.buttons.pressed(BUTTON_DOWN)) {
    counter = counter - 1;

    gb.display.setColor(RED);
    gb.display.print(counter);
    gb.sound.playCancel();
    
  }

  if (gb.buttons.pressed(BUTTON_MENU)) {
    counter = 0;
    gb.sound.playTick();
  }

  gb.display.setColor(BROWN);
  gb.display.fillRect(0, 0, counter, gb.display.height());

  gb.display.setColor(WHITE);
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter);
}


Le problème ici c'est que ça me l'affiche en vert ou rouge mais en petit dans le coin supérieur gauche. (normal vu que je n'ai précisé ni la taille ni l'emplacement mais si je le fait ça ne fonctionne pas..  J'ai en fait l'impression que l'affichage du compteur (en vert ou rouge) passe "derrière" le compteur blanc ainsi que le rectangle marron. Faut-il dire "d'enlever" le compteur blanc le temps que l'autre est affiché ou quelque chose comme ça ?

geed

6 years ago

C'est EXACTEMENT ça, imagine que la console affiche les choses dans l'ordre où tu lui demandes. Dans ton programmes tu changes la couleur AVANT de lui demander d'afficher ton compteur en blanc. Du coup, elle le change bien de couleur, mais le ré-affiche par dessus en blanc.


Perso je conditionnerai l'affichage du compteur.

C'est à dire que d'abord je fais mes calculs, puis dessine le "fond" et enfin je lui demanderai quelque chose du genre :

Si la précédente valeur est :

  • inférieure à la nouvelle, alors sélectionne le  vert
  • supérieure à la nouvelle, alors  sélectionne le rouge
  • identique, alors  sélectionne le blanc


Je te laisse méditer ces pistes, voir commencer à imaginer de petites fonctions pour le faire.

Perso je suis aussi débutant en programmation et je fonctionne comme ça : 1) je divise le problème en petits problèmes faciles 2) je code des petites fonctions qui les résolvent 3) j'assemble le tout :p

clement

6 years ago

je rajouterais que ton code est executer 25X par seconde. ton compteur vert ou rouge sera afficher uniquement 1/25 X du coup presque invisible.

Tu dois creer une variable pour sauvegarder la couleur courante de ton compteur 

geed

NEW 6 years ago

Lemmy Lemmy

C'est EXACTEMENT ça, imagine que la console affiche les choses dans l'ordre où tu lui demandes. Dans ton programmes tu changes la couleur AVANT de lui demander d'afficher ton compteur en blanc. Du coup, elle le change bien de couleur, mais le ré-affiche par dessus en blanc.


Perso je conditionnerai l'affichage du compteur.

C'est à dire que d'abord je fais mes calculs, puis dessine le "fond" et enfin je lui demanderai quelque chose du genre :

Si la précédente valeur est :

  • inférieure à la nouvelle, alors sélectionne le  vert
  • supérieure à la nouvelle, alors  sélectionne le rouge
  • identique, alors  sélectionne le blanc


Je te laisse méditer ces pistes, voir commencer à imaginer de petites fonctions pour le faire.

Perso je suis aussi débutant en programmation et je fonctionne comme ça : 1) je divise le problème en petits problèmes faciles 2) je code des petites fonctions qui les résolvent 3) j'assemble le tout :p

clement

NEW 6 years ago

Lemmy Lemmy

je rajouterais que ton code est executer 25X par seconde. ton compteur vert ou rouge sera afficher uniquement 1/25 X du coup presque invisible.

Tu dois creer une variable pour sauvegarder la couleur courante de ton compteur 

Lemmy

NEW 6 years ago

Geed ton enthousiasme fait plaisir à voir ! :D Je vois et vais essayer de partir dans ce sens là. Je vous retransmettrais mon nouveau code :)

Et Clément ce que tu dis j'y ai pensé aussi,1/25s c'est pas si pire, je veux vraiment que ça clignote mais si je peux le faire durer 2/25s voir 3/25s ça peut-être vraiment cool mais la... ça dépasse tout entendement pour mon niveau ! :)

Lemmy

NEW 6 years ago

Alors.. j'ai le bon affichage, à la bonne taille, etc mais ça s'affiche à côté de la valeur "blanche" du compteur. J'ai pas suivi le fonctionnement que tu m'a proposé Geed parce que j'avoue que je sais pas trop comment m'y prendre.. Dois-je continuer sur cette lancée ou aborder d'une autre manière ?

Si je continue sur ce que j'ai mit y a-t-il une solution de demander d'afficher la valeur verte ou rouge à la place de la blanche? Je ne sais pas trop comment faire... 


#include <Gamebuino-Meta.h>

int counter = 0;

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

void loop() {
  while (!gb.update());
  gb.display.clear();

  if (gb.buttons.pressed(BUTTON_UP)) {
    counter = counter + 1;
    gb.sound.playOK();
    
  }

  if (gb.buttons.pressed(BUTTON_DOWN)) {
    counter = counter - 1;
    gb.sound.playCancel();
    
  }

  if (gb.buttons.pressed(BUTTON_MENU)) {
    counter = 0;
    gb.sound.playTick();
  }

  gb.display.setColor(BROWN);
  gb.display.fillRect(0, 0, counter, gb.display.height());

  gb.display.setColor(WHITE);
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter);


  if (gb.buttons.pressed(BUTTON_DOWN)) {
  gb.display.setColor(RED);
  gb.display.print(counter);

}

  if (gb.buttons.pressed(BUTTON_UP)) {
  gb.display.setColor(GREEN);
  gb.display.print(counter);

}
}

clement

NEW 6 years ago

en fait tu n a pas besoin de reecrire le counter.

l idee c est au moment ou tu choisi la couleur ( a la ligne gb.display.setColor(WHITE);) tu doit choisir entre le rouge le vert ou le blanc

donc en mata code : 


si mon bouton haut est appuyer
     je choisi le vert
sinon si mon bouton bas est appuyer
    je choisi le rouge
sinon 
   je choisi le blanc


Pour cela tu doit te pencher sur le else  je ne sais pas si il y a des exemple dans les tuto Gamebuino mais tu trouvera facilement comment ca marche sur google.




Aurélien Rodot

6 years ago

Bientôt, bientôt, je suis justement en train d'écrire la suite :)

Mais ça fait plaisir de voir que c'est utilisé, que certains vont au delà et que tout le monde s'entraide. Keep it up ! :D

Lemmy

NEW 6 years ago

Ça ne marche pas, je suis à deux doigts d'aller chercher une masse afin de mieux faire comprendre à mon mac ce que je veux faire.. XD


J'ai été obligé de réécrire tout le conter pour qu'il apparaisse.

Si je met "if..." "else..." "else..." pour le deuxième "else" il me dit que ce n'est pas correct si il n'y a pas de "if" avant. Et avec ce code là quand j'appuie sur "haut" je vois le compteur qui clignote mais c'est trop rapide.. Impossible de dire s'il s'affiche en vert (il faut que je rallonge le temps d'affichage mais je ne sais pas comment) et par contre quand j'appuie sur "bas" le compteur change mais ne clignote pas.

Au moins il y a déjà l'affichage en vert qui fonctionne (peut-être) on va dire que c'est une avancée!

Voila ce que j'ai fait:

#include <Gamebuino-Meta.h>

int counter = 0;

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

void loop() {
  while (!gb.update());
  gb.display.clear();

  if (gb.buttons.pressed(BUTTON_UP)) {
    counter = counter + 1;
    gb.sound.playOK();
    
  }

  if (gb.buttons.pressed(BUTTON_DOWN)) {
    counter = counter - 1;
    gb.sound.playCancel();
    
  }d

  if (gb.buttons.pressed(BUTTON_MENU)) {
    counter = 0;
    gb.sound.playTick();
  }

  gb.display.setColor(BROWN);
  gb.display.fillRect(0, 0, counter, gb.display.height());

  if (gb.buttons.pressed(BUTTON_DOWN)) {
  gb.display.setColor(RED);
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter);

}

  if (gb.buttons.pressed(BUTTON_UP)) {
  gb.display.setColor(GREEN);
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter);

}
  else (gb.display.setColor(WHITE));
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter);


}

geed

6 years ago

C'est juste un problème de syntaxe ! Pas besoin de sortir la masse :)

regarde du côté des exemple sur l'arduino IDE, section CONTROL, tu verras que le else à une structure du type if (machin) { alors } else { bidule }

https://www.arduino.cc/reference/en/


Moi à ta place, je contournerai le problème, peut être avec une deuxième variable qui passe à 1 2 ou 3 en fonction de ce que tu fais. Et en fonction de la valeur de cette variable, tu changes l'affichages. Par exemple :)

Regarde aussi du côté de la fonction Switch (bidule) {


Et bon courage ! Ne décroche pas !



clement

6 years ago

oui tu ne peux pas mettre de else sur un seul if...

mais tu peux remetre un if dans un else 


Le switch que propose geed peux etre une bonne solution.


pour evité des problèmes a ton mac voici la syntax qui fonctionne du if / else qui fonctionnera pour ton cas. reste plus qu a mettre les bonne conditions dedans 

if() {
} else if () {
} else {
}  

Aurélien Rodot

NEW 6 years ago

clement clement

Bientôt, bientôt, je suis justement en train d'écrire la suite :)

Mais ça fait plaisir de voir que c'est utilisé, que certains vont au delà et que tout le monde s'entraide. Keep it up ! :D

geed

NEW 6 years ago

Lemmy Lemmy

C'est juste un problème de syntaxe ! Pas besoin de sortir la masse :)

regarde du côté des exemple sur l'arduino IDE, section CONTROL, tu verras que le else à une structure du type if (machin) { alors } else { bidule }

https://www.arduino.cc/reference/en/


Moi à ta place, je contournerai le problème, peut être avec une deuxième variable qui passe à 1 2 ou 3 en fonction de ce que tu fais. Et en fonction de la valeur de cette variable, tu changes l'affichages. Par exemple :)

Regarde aussi du côté de la fonction Switch (bidule) {


Et bon courage ! Ne décroche pas !



clement

NEW 6 years ago

Lemmy Lemmy

oui tu ne peux pas mettre de else sur un seul if...

mais tu peux remetre un if dans un else 


Le switch que propose geed peux etre une bonne solution.


pour evité des problèmes a ton mac voici la syntax qui fonctionne du if / else qui fonctionnera pour ton cas. reste plus qu a mettre les bonne conditions dedans 

if() {
} else if () {
} else {
}  

Lemmy

NEW 6 years ago

Merci, je vais essayer d'avancer la dessus ! :)


Edit:

Après une semaine sans toucher ma Gamebuino faute de temps :( j'ai suivi ton conseil Clement, et, même si je ne sais pas pourquoi ça fonctionne maintenant ! =)


#include <Gamebuino-Meta.h>

int counter = 0;

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

void loop() {
  while (!gb.update());
  gb.display.clear();

  if (gb.buttons.pressed(BUTTON_UP)) {
    counter = counter + 1;
    gb.sound.playOK();
    
  }

  if (gb.buttons.pressed(BUTTON_DOWN)) {
    counter = counter - 1;
    gb.sound.playCancel();
    
  }


if(gb.buttons.pressed(BUTTON_MENU)) {
    counter = 0;
    gb.sound.playTick();
  }

  gb.display.setColor(BROWN);
  gb.display.fillRect(0, 0, counter, gb.display.height());

  if (gb.buttons.pressed(BUTTON_DOWN)) {
  gb.display.setColor(RED);
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter);

} 
  
  else if (gb.buttons.pressed(BUTTON_UP)) {
  gb.display.setColor(GREEN);
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter);

}
  
  else {gb.display.setColor(WHITE));
  gb.display.setFontSize(4);
  gb.display.setCursor(8,8);
  gb.display.print(counter);


}


J'aimerais encore faire quelques modifications comme par exemple rallonger la durée de couleur rouge ou verte mais je ne sais pas comment..

et aussi j'aimerai ajouter un "flash" mais je ne connais pas la "fonction" ?? pour allumer les led "gb.display.lesledcestjoliquandcestallumé" par exemple.. :)


Vous avez déjà été formidables avec tous vos conseils mais j'en veux plus !! 

Nicolas

NEW 5 years ago

Hello ! Super tuto :) 

Au début il est écrit "Lorsque l'on éteint la console, la valeur est perdue." Comment faire justement pour garder en mémoire la valeur ? J'ai bien envie de faire un compteur qui peut me servir dans le temps et donc ne pas perdre la valeur ;-) 

J'ai été regarder dans la référence, et j'ai vu qu'il y avait une rubrique "save" :

gb.save.config  
gb.save.get 
gb.save.set  
gb.save.del

Merci pour vos conseils !

Sorunome

5 years ago

Yes, you save values using the gb.save stuff, that will actually store the values on the micro SD card. You can find more on how to do that here: https://gamebuino.com/creations/gamebuino-save-format

Sorunome

NEW 5 years ago

Nicolas Nicolas

Yes, you save values using the gb.save stuff, that will actually store the values on the micro SD card. You can find more on how to do that here: https://gamebuino.com/creations/gamebuino-save-format

Nicolas

5 years ago

Merci !

Je viens de faire un test et je pense que je ne vois pas bien comment sauvegarder la valeur avec gb.save

Voici mon code :

#include <Gamebuino-Meta.h>

int compteur = 0;


void setup() {
  gb.begin();

}

void loop() {
  while (!gb.update());
  gb.display.clear();



  if (gb.buttons.pressed(BUTTON_UP)) {  
    compteur = compteur + 1;    
    gb.sound.playOK(); 
  } 


  gb.display.print(compteur);

  gb.save.get(compteur);

}

J'ai bien ajouté gb.save.get avec la variable "compteur".

Merci pour le coup de pouce !