Jogue Snake com Arduino! - Jogo da cobrinha - MakerHero

Jogue Snake com Arduino! 35

Todo mundo que é ligado em tecnologia (ou teve um celular Nokia daqueles antigos) já jogou algum dia o famoso jogo da cobrinha (Snake). Existem várias versões online desse jogo clássico, e vamos mostrar como você pode jogar o Snake com Arduino, montando um circuito com poucos componentes.

Protótipo montado do Jogo da Cobrinha com Arduino.

Para quem não conhece o jogo, ele nada mais é do que uma “cobrinha” que percorre a tela atrás de ovos. A cada ovo que ela consegue encontrar, o placar é incrementado em um ponto, a cobra ganha mais um “círculo”, aumentando de tamanho, e aumenta também a velocidade do jogo. Caso a cobra encoste nas laterais da tela ou em si própria, o jogo é encerrado.

Circuito Arduino Snake – Jogo da Cobrinha

O circuito utiliza um Arduino Uno e 4 botões do tipo push-button para controlar a movimentação da cobra na tela. Se preferir, você pode substituir esses botões por um Joystick 3 Eixos, economizando assim 2 portas do Arduino.

O display é o Nokia 5110, com resolução de 84×64 pixels e backlight azul. Não se esqueça que a tensão máxima no pino BL (backlight) é de 3.3V.

Para a montagem do circuito do jogo utilizamos o seguinte material:

O circuito pode ser montado em uma protoboard mas você pode ver pelas imagens anteriores que optamos por utilizar uma placa de fenolite perfurada, escondendo os fios na parte traseira. Isso facilitou o acesso aos botões, melhorando a jogabilidade.

Esquema de ligação do Arduino Uno com o Display, botões e buzzer utilizando uma protoboard.

Para facilitar a montagem e reduzir o número de componentes, ativamos os resistores pull up internos nas portas utilizadas pelos botões (4, 5, 6 e 7). A outra extremidade dos botões é ligada ao GND.

Programa Snake com Arduino

Para compilar esse programa, você vai precisar das bibliotecas Adafruit_GFX (download) e Adafruit_PCD8544 (download). Faça o download das bibliotecas, descompacte os arquivos e coloque as duas bibliotecas dentro da pasta Libraries da IDE do Arduino.

//Programa: Snake com Arduino Uno
//Alteracoes e adaptacoes: MakerHero
//
//Baseado no codigo original de Abhinav Faujdar *****/

#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <SPI.h>

//Inicializa o display
Adafruit_PCD8544 display = Adafruit_PCD8544(8, 9, 10, 11, 12);

#define LEFT 7
#define DOWN 5
#define RIGHT 4
#define UP 6
#define speakerPin 17

//Verifica em qual direcao a cobra esta se movendo
boolean dl = false, dr = false, du = false, dd = false;

int x[50], y[50], i, slength, tempx = 10, tempy = 10, xx, yy;
unsigned int high;
uint8_t bh, bl;
int xegg, yegg;
int freq, tb;
int l, r, u, d, p;
unsigned long time = 280, beeptime = 50;
int score = 0, flag = 0;

void setup()
{
  Serial.begin(9600);
  display.begin();

  //Informacoes iniciais display
  display.setContrast(50);
  display.clearDisplay();
  display.drawRoundRect(0, 0, 84 , 25, 1, 2);
  display.setTextSize(2);
  display.setTextColor(BLACK);
  display.setCursor(12, 6);
  display.println("SNAKE");
  display.setTextSize(1);
  display.setTextColor(BLACK);
  display.setCursor(12, 29);
  display.println("MakerHero");
  display.setCursor(13, 29);
  display.println("MakerHero");
  display.setCursor(0, 40);
  display.println("makerhero.com");

  display.display();
  delay(2000);

  pinMode(LEFT, INPUT);
  pinMode(RIGHT, INPUT);
  pinMode(UP, INPUT);
  pinMode(DOWN, INPUT);
  pinMode(speakerPin, OUTPUT);
  digitalWrite(LEFT, HIGH);
  digitalWrite(RIGHT, HIGH);
  digitalWrite(UP, HIGH);
  digitalWrite(DOWN, HIGH);

  slength = 8;
  xegg = (display.width()) / 2;
  yegg = (display.height()) / 2;
  display.clearDisplay();
  display.drawRect(0, 0, 84, 48, 1);
  display.setCursor(4, 2);
  display.print("P:     R:");
  display.setCursor(22, 2);
  display.print(score);
  display.setCursor(64, 2);
  display.print(high);
  display.drawRect(0, 0, 84, 11, 1);
  //Define coordenadas iniciais
  for (i = 0; i <= slength; i++)
  {
    x[i] = 25 - 3 * i;
    y[i] = 30;
  }
  for (i = 0; i < slength; i++)  //Draw the snake
  {
    display.drawCircle(x[i], y[i], 1, BLACK);
  }
  display.display();
  dr = true;
}

void loop()
{
  movesnake();
}

void movesnake()
{
  //Leitura do valor dos botoes
  l = digitalRead(LEFT);
  d = digitalRead(DOWN);
  r = digitalRead(RIGHT);
  u = digitalRead(UP);

  if (flag == 0)
  {
    direct();
  }

  if (millis() % time == 0)
  {
    if (flag == 0)
    {
      if (dr == true) {
        tempx = x[0] + 3;
        tempy = y[0];
      }
      if (dl == true) {
        tempx = x[0] - 3;
        tempy = y[0];
      }
      if (du == true) {
        tempy = y[0] - 3;
        tempx = x[0];
      }
      if (dd == true) {
        tempy = y[0] + 3;
        tempx = x[0];
      }
    }
    flag = 0;
    //Verifica se a cobra esta nas mesmas coordenadas do ovo
    checkgame();
    checkegg();

    //Altera as coordenadas de todos os pontos da cobra
    for (i = 0; i <= slength; i++)
    {
      xx = x[i];
      yy = y[i];
      x[i] = tempx;
      y[i] = tempy;
      tempx = xx;
      tempy = yy;
    }
    //Desenha a cobra e o ovo nas novas coordenadas
    drawsnake();
  }
}

void checkgame()
{
  for (i = 1; i < slength; i++)
  {
    //Verifica se o recorde foi batido e
    //mostra na tela o novo valor
    high = (((0xff00 + bh) << 8) + bl);
    if (score > high)
    {
      high = score;
      bh = (high >> 8);
      bl = high & 0xff;
      display.fillRect(61, 1, 20, 9, 0);
      display.setCursor(65, 2);
      display.print(high);
    }
    //Verifica se tocou nas bordas
    if ((x[0] <= 1 || x[0] >= 83) || (y[0] <= 12 || y[0] >= 47) || (x[i] == x[0] && y[i] == y[0]) )
    {
      //Jogo terminado. Mostra informacoes na tela
      display.clearDisplay();
      display.fillRoundRect(0, 0, 84 , 31, 1, 2);
      display.setTextColor(WHITE);
      display.setTextSize(2);
      display.setCursor(18, 1);
      display.print("GAME");
      display.setCursor(18, 16);
      display.print("OVER");
      display.setTextColor(BLACK);
      display.setTextSize(1);
      display.setCursor(12, 33);
      display.print("PLACAR");
      display.setCursor(60, 33);
      display.print(score);
      display.setCursor(12, 41);
      display.print("RECORDE");
      display.setCursor(60, 41);
      display.print(high);
      display.display();
      //Aguarda 5 segundos e reinicia o jogo
      delay(5000);

      //Apaga a tela
      display.clearDisplay();
      //Retorna aos valores iniciais
      slength = 8;
      score = 0;
      time = 280;
      redraw();
    }
  }
}

void checkegg()      //Snake meets egg
{
  //Verifica se a cobra está nas mesmas coordenadas do ovo
  if (x[0] == xegg or x[0] == (xegg + 1) or x[0] == (xegg + 2) or x[0] == (xegg - 1))
  {
    if (y[0] == yegg or y[0] == (yegg + 1) or y[0] == (yegg + 2) or y[0] == (yegg - 1))
    {
      //Incrementa a cobra em uma posição, incrementa o placar e
      //aumenta a velocidade do jogo, diminuindo o tempo
      score += 1;
      display.fillRect(21, 1, 20, 9, 0);
      display.setCursor(22, 2);
      display.print(score);
      slength += 1;
      if (time >= 90)
      {
        time -= 10;
      }
      display.fillRect(xegg, yegg, 3, 3, WHITE);
      display.display();
      beep(35, beeptime);
      //Cria novo ovo em posicao aleatoria
      xegg = random(2, 80);
      yegg = random(15, 40);
    }
  }
}

void direct()
{
  //Altera o movimento caso uma tecla tenha sido pressionada
  if (l == LOW and dr == false)
  {
    dl = true; du = false; dd = false;
    //Salva novas coordenadas em Tempx, Tempy
    tempx = x[0] - 3;
    tempy = y[0];
    flag = 1;
  }
  else if (r == LOW and dl == false)
  {
    dr = true; du = false; dd = false;
    tempx = x[0] + 3;
    tempy = y[0];
    flag = 1;
  }
  else if (u == LOW and dd == false)
  {
    du = true; dl = false; dr = false;
    tempy = y[0] - 3;
    tempx = x[0];
    flag = 1;
  }
  else if (d == LOW and du == false)
  {
    dd = true; dl = false; dr = false;
    tempy = y[0] + 3;
    tempx = x[0];
    flag = 1;
  }
}

void drawsnake()
{
  display.fillRect(xegg, yegg, 3, 3, BLACK);
  display.drawCircle(x[0], y[0], 1, BLACK);
  display.drawCircle(x[slength], y[slength], 1, WHITE);
  display.display();
}

void redraw()
{
  display.drawRect(0, 0, 84, 48, 1);
  display.drawRect(0, 0, 84, 48, 1);
  display.setCursor(4, 2);
  display.print("P:     R:");
  display.drawRect(0, 0, 84, 11, 1);
  display.fillRect(21, 1, 20, 9, 0);
  display.setCursor(22, 2);
  display.print(score);
  display.fillRect(61, 1, 20, 9, 0);
  display.setCursor(65, 2);
  display.print(high);

  xegg = (display.width()) / 2;
  yegg = (display.height()) / 2;
  dl = false, dr = false, du = false, dd = false;
  dr = true;
  display.setCursor(4, 2);
  display.print("P:     R:");
  display.drawRect(0, 0, 84, 11, 1);
  //Retorna as coordenadas iniciais
  for (i = 0; i <= slength; i++)
  {
    x[i] = 25 - 3 * i;
    y[i] = 30;
  }
  tempx = 33 - 3 * i;
  tempy = 30;
  display.display();
}

void beep (int freq, long tb)
{
  int x;
  long delayAmount = (long)(500 / freq);
  long loopTime = (long)(tb / (delayAmount * 2));
  for (x = 0; x < loopTime; x++)
  {
    digitalWrite(speakerPin, HIGH);
    delay(delayAmount);
    digitalWrite(speakerPin, LOW);
    delay(delayAmount);
  }
  delay(2);
}

O programa é baseado no programa original encontrado neste link. Fizemos algumas alterações no código, limitando a área do jogo para que o placar e o recorde pudessem ser mostrados na parte superior da tela.

Outra alteração no programa evita que a cobra possa ultrapassar as laterais do display. Caso isso aconteça, é acionada a rotina que encerra o jogo e mostra o placar do último jogo e o recorde atual.

Tela do jogo Snake com os dizeres Game Over

 Gostou? Deixe seu comentário logo abaixo.

Faça seu comentário

Acesse sua conta e participe

35 Comentários

  1. A ligação do display é a mesma para o vermelho?

    1. Olá, Brener,

      Se possuir a mesma pinagem, é para funcionar.

      Abraços!
      Diogo – Equipe MakerHero

  2. Show a explicação

    1. Olá,

      É muito bom saber que gostou do nosso trabalho 😀

      Abraço!
      Rosana – Equipe MakerHero

  3. Seria muito bom se vocês deixassem o link para baixar o arquivo código exemplo. Porque muitas vezes pelo menos comigo da erro ao copiar
    Obrigado
    S. E. G.

    1. Olá Silvio!

      Como que ele tem ficado na IDE depois de colar? Exatamente igual ao post? Às vezes pega coisas a mais ou a menos na hora de copiar.

      Abraços!

      André – Equipe MakerHero

  4. se for usar Standalone os pinos se mantem os mesmos???

    1. Olá Daniel!

      O que você quis dizer com Standalone?

      Abraços!

      André – Equipe MakerHero

  5. o buzzer não funcionou?

    1. Olá Rafael!

      O buzzer funciona em outros projetos?

      Ele é ativo?

      Abraços!

      André – Equipe MakerHero

  6. Eu utilizei o display nokia 5110 vermelho. O tive que modificar um pouco. Alimentei o VCC com 3.3V e não com 5V. O pino 7 do display, light eu liguei no terra. O contraste ficou melhor em 40 (display.setContrast(40)).

    1. Hmmm BARÇA é campeão!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  7. Tem como eu conectar esse display no meu Raspberry pi zero w?se sim,gostaria de saber as pinagens certas onde colocar,obrigado.

  8. Bom noite!

    Como ficaria o código para a utilização de módulo joystick no lugar dos push botton?

    att. Diana Rose

    1. Bom dia, também queria saber como ficava a ligação feita e o código se altera com o joystick?

      Obrigada

  9. Bom dia!

    Como ficaria o codigo para a utilização de joystick 5 pinos no lugar dos push botton?

    att.

    Hugo

  10. buttons not working what should i do?

    Botões que não funcionam o que devo eu fazer?

  11. Wow…for en plass dere har der. Og vil tro som du sier,at sommer'n der mÃ¥ være fantastisk.Selv har vi hytte pÃ¥ Nesodden…Ganske deilig der ute ogsÃ¥ :o)NÃ¥ sitter vi i heimen og koser oss med Fangene pÃ¥ Fortet!Ha en super uke.Klem Hege

  12. O código está com erro na linha 94, ‘movesnake’ was not declared in this scope. Estou fazendo o projeto e deu esse erro :(((

  13. Alguem fez e deu certo?

  14. Olá,
    Muito legal o jogo, gostaria de saber com quem posso entrar em contato pois tenho muitos displays iguais a este, gostaria de oferecer para a loja de vocês. (Preço de ocasião para liberar espaço que tenho aqui)
    abs!
    Marcelo

  15. Muito bom!!
    Pretendo testar assim que tiver os componentes necessários.

  16. Boa Noite.

    Estou com alguns problemas para adicionar as bibliotecas, pois baixando elas pelo link indicado executando os passos o Arduíno não consegue encontrá-las.Se puder Ajudar desde já agradeço.

    Att, Eduarda

    1. O meu encontra apenas uma delas.

    2. Se alguém puder dar uma dica de como resolver, ficarei grato.

  17. Muito bom, mas posso trocar por um joystick só de dois eixos? Se sim, quais seriam as alterações no código?

  18. hi,my screen light up but no writing appears what do i do?

  19. Pode utilizar qualquer display?

    1. Bom dia Leticia,

      O jogo é relativamente simples, mas você vai ter que adaptar os comandos para cada tipo de display.

      Abraço!

      Adilson – Equipe MakerHero

  20. Boa Noite. Como posso fazer para criar processos no arduino? isto é, ter o jogo a correr e, ao mesmo tempo usar o buzzer para por musica.

    e já agora… e possivel usar o arduino nano neste projeto?

  21. Muito bom.
    Há tempo tenho procurado alguma programação que funcionasse..

    Gostaria de saber se vocês podem disponibilizar a imagem do Display Nokia 5110 para montagem no Fritzing, pois a que eu tenho é o vermelho.

    Atenciosamente,
    Henrique

Trackbacks and Pingbacks