Jogo 5 : Shot game - roleta de leds

Objetivo

Neste projeto vamos criar um jogo simples, divertido e que requer apenas uma pessoa para jogar. O objetivo do jogo é acertar o led verde ou pelo menos um dos leds laranja ou amarelo. Para acertar um led é necessário clicar o botão (push button) no momento em que o led estiver aceso. Cada partida terá 5 jogadas e a pontuação será a soma dos pontos de cada jogada, sendo: led verde = 50 pontos, led laranja = 20 pontos e led amarelo = 10 pontos. A velocidade do jogo também aumenta a cada jogada. Portanto fique ligado e bata o seu recorde!

Aplicação

Para fins didáticos e diversão.

Componentes necessários

Referência

Componente

Quantidade

Imagem

Observação

Protoboard Protoboard 830 pontos 1 Resultado de imagem para protoboard 830v

No mínimo utilizar protoboard com 830 pontos

Jumpers Kit cabos ligação macho / macho 1    
Led Difuso 5mm Led 5mm 9   Utilizar de preferência: 4 leds vermelhos, 2 leds amarelos, 2 leds laranjas e 1 led verde
Resistor Resistor de 150 Ω maior
9   Se precisar usar outros valores, calcule o resistor apropriado para o led ou barra grafica utilizada - Calcular Resistor.
Push Button Push button 6X6X5mm 1    
Capacitor Cerâmico  Capacitor Cerâmico 2nF a 10nF  1  3 Pçs 2.2nf X 2kv Capacitor 222 2200 105°c 2kv Capacitores - R$ 21 ... O capacitor será utilizado para estabilizar a função do botão (push button) no caso de se utilizar interrupção externa. 
Arduino UNO R3 Arduino UNO 1

Você poderá utilizar uma placa Arduino UNO original ou similar

Montagem do Circuito

Conecte os componentes no Protoboard como mostra a figura abaixo. Verifique cuidadosamente os cabos de ligação antes de ligar seu Arduino. Lembre-se que o Arduino deve estar totalmente desconectado da força enquanto você monta o circuito.


Atenção

1) Lembre-se que o LED tem polaridade: O terminal maior tem polaridade positiva e o lado do chanfro tem polaridade negativa.

2) Determinamos o valor do resistor através da tabela prática: Tabela prática de utilização de leds 3mm e 5mm. Entretanto, o mais correto é sempre verificar o datasheet do fabricante do LED para você ter os exatos valores de tensão e corrente do mesmo - leia Como calcular o resistor adequado para o led. (Obs.: Resistores iguais ou superiores a 150 Ω poderão ser utilizados em LEDs de todas as cores para um circuito com tensão igual ou inferior a 5V).

4) Monte o botão (push button) sem o resistor, pois através da programação vamos habilitar o resistor pull-up interno do arduino. Desta forma, quando o botão estiver pressionado, o Arduino retornará "LOW" ou "0". Veja o tutorial: Como usar push button com Arduino (programação)

5) Como vamos configurar uma interrupção externa neste projeto, precisaremos utilizar um capacitor cerâmico para evitar o efeito "bouncing" no push button.

5.1. Como a interrupção externa gera uma ação muito rápida, somente o delay utilizado no software não será suficiente para evitar as oscilações geradas pelo efeito "bouncing". Recomendo utilizar um capacitor cerâmico de 2,0nF a 10nF na montagem do botão. Utilizamos no nosso projeto um capacitor de 2,2nF. Veja abaixo como conectar o capacitor cerâmico.

5.2. O efeito "bouncing" ocorre sempre quando trabalhamos com chaves mecânicas. Ele é responsável por gerar equivocadamente sinais como se ocorressem diversos acionamentos em um pequeno intervalo de tempo:

5.2.1. Veja no gráfico abaixo o exemplo deste efeito. Observe que ao pressionarmos um botão, por exemplo, ocorre algumas oscilações rápidas com idas e vindas do nível lógico alto e do nível lógico baixo, antes da estabilização das partes mecânicas do componente. Estas oscilações podem gerar informações indevidas ao nosso microcontrolador, que interpretará que o botão foi pressionado rapidamente várias vezes, quando na verdade foi pressionado apenas um vez.

Bouncing

5.3. Debouncing são técnicas que utilizamos para resolver este problema. O "debouncing" consiste basicamente em atrasar o código, através do software, com o uso da função delay(), função delayMicroseconds(), função millis(), ou através do hardware, com a instalação de um capacitor ou ainda através de bibliotecas específicas.

5.3.1. Como configuramos uma interrupção externa para atuar em uma chave mecânica (push button, por exemplo), o sinal será enviado diretamente no pino do microcontrolador quando pressionamos o botão. Por isso, para eliminarmos os efeitos do "bouncing" de forma mais eficiente precisamos criar um atraso primeiro no hardware, usando o capacitor cerâmico e depois no software, usando a função delayMicroseconds() que funcionará em uma ISR (Interrupt Service Routine, ou Rotina de Serviço de Interrupções em português).

6. Como vamos utilizar neste projeto efeitos de iluminação sequencial por leds e interrupção externa, recomendo que leia os seguintes tutoriais:

7. Veja abaixo como ficou a montagem do nosso projeto:

Código do Projeto (Sketch)

1. Faça o dowload e abra o arquivo jogo5.ino no IDE do Arduino: DOWNLOAD jogo5.ino

2. Se preferir, copie e cole o código abaixo no IDE do Arduino:

/*******************************************************************************
*
*  Jogo 5 - Jogo da Roleta (shot game)
*  Adaptado por Angelo Luis Ferreira
*            05/02/2021
*    http://squids.com.br/arduino
*
*******************************************************************************/
// =======================================================================
//             Definições
// led pino 7 = verde (50 pontos) - currentLED=4
// led pino 6 e led pino 8 = amarelo (20 pontos) - currentLED = 3 e 5
// led pino 5 e led pino 9 = laranja (10 pontos) - currentLED = 2 e 6
// demais leds = vermelho (0 pontos)
//
// =====================================================================         

// biblioteca LCS com I2C
#include <LiquidCrystal_I2C.h>

// Inicializa o display no endereco 0x27
LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3, POSITIVE);

// hardware
byte ledPin[] = {3,4,5,6,7,8,9,10,11}; // leds
byte butPin = 2; // push button

// variáveis
int shot = 0;
boolean gameStatus = false;
boolean gameOverStatus = false;
byte currentLED = 0;
int dirFlag = 1;
int points = 0;
int record = 0;

void setup(){
  // define hardware - entradas e saídas
  for (byte i = 0; i<9; i++) {
    pinMode(ledPin[i], OUTPUT);
  }
  pinMode(butPin, INPUT_PULLUP);
  // inicia monitor serial e display LCD
  Serial.begin(9600);
  lcd.begin (16,2);
  // inicia interrupção externa
  attachInterrupt(digitalPinToInterrupt(butPin), buttonClick, FALLING);
  // tela inicial
  showStart(); // exibe display para iniciar jogo
  allLedsOn(); // acende todos os leds 
  Serial.println("================================");
  Serial.println("Novo Jogo - Clique para iniciar");      
} // end setup

void loop() {
 if (!gameStatus) {
    if (gameOverStatus == true) {
      gameOver();
    } else if (gameOverStatus == false)  {
      allLedsOff();
      checkButton();
    }
 } else if (gameStatus){     
  moveLed();   
 }

} //end loop

// ========================== JOGANDO =====================================

void moveLed() {    
    showGame(); // exibe pontos no lcd    
    digitalWrite(ledPin[currentLED], HIGH); // acende o LED atual
    if (shot >0 && shot<6) delay(100/(6-shot));  // dificuldade do jogo     
    currentLED += dirFlag; // incrementa de acordo com o valor de direction    
    allLedsOff();
    // altera a direção se tivermos atingido o fim
    if (currentLED == 9) dirFlag = -1;
    if (currentLED == 0) dirFlag = 1;

    if (shot<=0) { // sem vidas
      gameStatus = false;
      gameOverStatus = true;     
   }
}

// ao clicar o botão durante o jogo
void buttonClick() {      
    // calcula pontuação
    if (currentLED == 4) points += 50;
    if (currentLED == 3 || currentLED == 5) points += 20;
    if (currentLED == 2 || currentLED == 6) points += 10;
    showGameMonitor(); // exibe pontos no monitor 
    // calcula jogadas     
    if (gameStatus == true) {
      shot--;             
  }
  delayMicroseconds(50000); // debouncing
}

void showGameMonitor() {
  if (shot > 0) {
  Serial.print(currentLED); // exibe o led que foi clicado
  if (currentLED == 4) {
      Serial.println(" | Acertou o Led verde -> 50 pontos - SHOT!!!");
    } else if (currentLED == 3 || currentLED == 5){
      Serial.println(" | Acertou o led laranja -> 20 pontos");
    } else if (currentLED == 2 || currentLED == 6){
      Serial.println(" | Acertou o led amarelo -> 10 pontos");
    } else Serial.println(" | Acertou o led vermelho -> 0 pontos");
  }  
}

  // exibe no lcd
void showGame(){
  // pontos
  lcd.setCursor(0,0);
  lcd.print("Pontos: ");
  lcd.setCursor(9,0);
  lcd.print(points);
  
  // shot
  lcd.setCursor(0,1);
  lcd.print("Shot: ");
  lcd.setCursor(6,1);
  lcd.print(shot);

  // record
  lcd.setCursor(9,1);
  lcd.print("R= ");
  lcd.setCursor(12,1);
  lcd.print(record);
}

//================= Tela inicialização ======================

// clique para iniciar jogo
void showStart() {
  for (byte i=0;i<3;i++) {
    lcd.setBacklight(HIGH);
    lcd.setCursor(2,0);
    lcd.print("CLIQUE PARA");
    lcd.setCursor(1,1);
    lcd.print("INICIAR O JOGO!");
    delay(300);
    lcd.setBacklight(LOW);
    delay(300);
  }
   lcd.setBacklight(HIGH);
}

// acende tods leds
void allLedsOn() {
  for (byte i = 0; i<9; i++) {
    digitalWrite(ledPin[i], HIGH);    
  }
  delay(1000);
}

// apaga todos leds
void allLedsOff() {
   for (byte i = 0; i<9; i++) {
    digitalWrite(ledPin[i], LOW);  
  }
}

// verifica se o botão foi acionado
void checkButton() {
  if (!digitalRead(butPin) && gameStatus == false) {
    if(shot == 0) {
      shot = 5;
      gameStatus = true;
      lcd.clear(); 
    }
    while(!digitalRead(butPin)) {}
    delay(50);
  }
}

//================ GAME OVER =================
void gameOver() {
 // imprime no monitor serial
 Serial.print("Game Over | ");
 Serial.print("Seus Pontos: "); 
 Serial.println(points);
 if (points > record) Serial.println(" *** NOVO RECORDE *** ");
 Serial.println("================================");
 Serial.println("Novo Jogo - Clique para iniciar");
 // imprime no display
 lcd.clear();
 lcd.setCursor(3,0);
 lcd.print("GAME OVER");
 lcd.setCursor(0,1);
 lcd.print("SEUS PONTOS: ");
 lcd.setCursor(12,1);
 lcd.print(points);
 delay(5000);
 lcd.clear();
 
 // seta record
 if (points > record) {
  record = points;
  recordShow();
 } else {
   
 // tela inicial
  showStart(); // exibe display para iniciar jogo
  allLedsOn(); // acende todos os leds 
 }

  // set variáveis
 shot = 0;
 gameStatus = false;
 gameOverStatus = false;
 currentLED = 0;
 points = 0;
 dirFlag = 1;
 
}

// ==================== Record Show ================
void recordShow() {
for (byte i=0;i<3;i++) {
    lcd.setBacklight(HIGH);
    lcd.setCursor(1,0);
    lcd.print("*NOVO RECORD*");
    lcd.setCursor(2,1);
    lcd.print(points);
    lcd.setCursor(6,1);
    lcd.print(" Pontos");
    delay(500);
    lcd.setBacklight(LOW);
    delay(500);
  }
   lcd.setBacklight(HIGH);
   delay(2000);
   lcd.clear();
   // tela para inicializar jogo
   lcd.setCursor(2,0);
   lcd.print("CLIQUE PARA");
   lcd.setCursor(1,1);
   lcd.print("INICIAR O JOGO!");
   allLedsOn();
   delay(500);  
}

Vídeo

Como o projeto deve funcionar

1. Ao executar o programa, aparecerá no display a mensagem "Clique para iniciar o jogo".

2. Para acompanhar qual led foi acionado, abra também o Monitor Serial.

3. Clique no botão (push button) para iniciar o jogo.

3.1. Os leds se acenderão um a um, da esquerda para a direita e depois da direita para a esquerda (sequência vai e volta).

3.2. O objetivo do jogo é clicar rapidamente no momento exato quando o led verde esteja aceso.

3.2.1. Se você acertar (clicar) o led verde, ganhará 50 pontos;

3.2.2. Se acertar os leds próximos, amarelo, 20 pontos e o laranja 10 pontos.

3.3. Cada partida possui 5 jogadas (shots), ou seja, você poderá tentar acertar o led verde, amarelo ou laranjado 5 vezes.

3.4. A velocidade da sequência de leds irá aumentando a cada jogada.

3.5. A pontuação do jogo será a soma dos pontos que você recebeu por cada led acertado.

3.6. A pontuação será mostrada no display lcd, e no Monitor Serial como mostra a figura abaixo:

 

3.7. No display LCD vamos visualizar os pontos acumulados na partida, o número de jogada faltantes e o recorde a ser batido.