Jogo do Dinossauro do Chrome no Arduino
E aí, gente! Tudo certo? No post de hoje vamos fazer um projeto bem simples e bem interessante no Arduino: O JOGO DO DINOSSAURO DO GOOGLE CHROME!
É isso mesmo que vocês leram! E não, não é uma automação do jogo utilizando o Arduino. Nós iremos criar nós mesmos o jogo do dinossauro do chrome no Arduino!
![]() |
| Demonstração do jogo. Fonte: Autoria Própria (2021) |
Para a criação desse projeto, nós precisaremos de:
- 1 Arduino
- 1 Display LCD 16x2
- 1 Push Button
- 1 Potenciômetro de 10kΩ
- 1 Resistor de 10kΩ
- 1 Resistor de 330Ω
- 1 Buzzer (opcional)
- Vários Jumpers
Para realizarmos a montagem, vamos utilizar o seguinte esquemático:
![]() |
| Esquema da montagem do jogo do chrome. Fonte: Autoria Própria (2021) |
Para facilitar a visualização das conexões, vou deixar uma tabela aqui embaixo onde vocês poderão encontrar onde cada pino deverá ser ligado.
| Pino - Componente | Função | Conectar em |
|---|---|---|
| 1 - LCD | Vss (alimentação negativa) | GND |
| 2 - LCD | Vdd (alimentação positiva) | VCC/5V |
| 3 - LCD | V0 (contraste) | Pino central do potenciômetro |
| 4 - LCD | RS (seleção de comandos ou dados) | Pino 12 do Arduino |
| 5 - LCD | RW (leitura ou escrita) | GND |
| 6 - LCD | E (pino de ativação) | Pino 11 do Arduino |
| 7 - LCD | DB0 (data bit 0) | - |
| 8 - LCD | DB1 (data bit 1) | - |
| 9 - LCD | DB2 (data bit 2) | - |
| 10 - LCD | DB3 (data bit 3) | - |
| 11 - LCD | DB4 (data bit 4) | Pino 5 do Arduino |
| 12 - LCD | DB5 (data bit 5) | Pino 4 do Arduino |
| 13 - LCD | DB6 (data bit 6) | Pino 3 do Arduino |
| 14 - LCD | DB7 (data bit 7) | Pino 2 do Arduino |
| 15 - LCD | A (ânodo do backlight) | VCC com resistor de 330Ohm |
| 16 - LCD | K (cátodo do backlight) | GND |
| Pino da Esquerda - Potenciômetro | Alimentação negativa | GND |
| Pino Central - Potenciômetro | Controle de Contraste | Pino 4 do LCD |
| Pino da Direita - Potenciômetro | Alimentação positiva | VCC |
| Pino 1 - Push Button* | Alimentação positiva | VCC |
| Pino 2 - Push Button* | Envio do sinal | Pino 9 do Arduino e GND com resistor de 10kOhm |
| Positivo - Buzzer | Sinal de ativação e controle do som | Pino 8 do Arduino |
| Negativo - Buzzer | Alimentação negativa | GND |
*Perceba que, no push button, existem 4 pinos, porém quando se olha para eles em pares, você pode perceber que dois pinos estão mais pertos um do outro esses são os pinos 1 e 2, independente da ordem, pois os pinos que estão mais distantes são uma extensão desses pinos. Veja a imagem abaixo para entender melhor
![]() |
| Indicação dos pinos do push button. Fonte: Autoria Própria (2021) |
Os pinos de DB4 a DB7 são utilizados tanto nas interfaces de 4 bits, que é a que está sendo utilizada, quanto nas de 8 bits, porém, nesta última configuração, todos os pinos de data bit são utilizados. Os pinos 15 e 16 do LCD estão relacionados com a alimentação do backlight, isto é, a luz de fundo, e o ânodo foi colocado com um resistor para limitar a corrente que passa na luz de fundo.
Lembrando que o buzzer é completamente opcional, mas é um toque a mais bem interessante.
O código ficou um pouco grande, porém é fácil de entender! Vamos dar uma olhada:
#include <LiquidCrystal.h>
#define JUMP 9
#define BUZZER 8
#define MAX_JUMP_TIME 1200
#define POINTS_SPEED2 400
#define POINTS_SPEED3 900
#define POINTS_SPEED4 1500
#define CACTUS_GEN_CHANCE_THRESHOLD 60
#define CACTUS_GEN_TIME 800
#define CACTUS_STYLE 3
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // RS, E, DB4, DB5, DB6, DB7
byte dino[] = {
B01110,
B01011,
B01111,
B00110,
B10111,
B11110,
B01110,
B01010
};
byte cactus1[] = {
B00100,
B10100,
B10101,
B10101,
B11101,
B00111,
B00100,
B00100
};
byte cactus2[] = {
B00100,
B10101,
B10101,
B10101,
B11111,
B00100,
B00100,
B00100
};
byte cactus3[] = {
B00100,
B00101,
B10101,
B10101,
B10111,
B11100,
B00100,
B00100
};
byte block[] = {
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111
};
bool jumping = false, started = false;
unsigned int playerY = 1;
unsigned long jumpTime = 0, lastDraw = 0, lastGenerated = 0, startTime = 0;
int cacti[16], cactiCount = 0;
int cactiSpeed = 500; //500ms (slow) -> 200ms (fast)
void clearPlayer(){
lcd.setCursor(4,playerY);
lcd.print(" ");
}
void gameOver(){
started = false;
clearCacti();
lcd.clear();
lcd.setCursor(3,0);
lcd.print("GAMEOVER");
lcd.setCursor(3,1);
lcd.print((millis()-startTime)/100);
tone(BUZZER, 415);
delay(80);
tone(BUZZER, 302);
delay(50);
noTone(BUZZER);
delay(400);
}
void allBlocks(){
for(int i = 0; i < 16; i++)
for(int j = 0; j < 2; j++){
lcd.setCursor(i,j);
lcd.write(byte(4));
}
}
void checkSpeed(){
if((millis()-startTime)/100 >= POINTS_SPEED2 && cactiSpeed > 400){
cactiSpeed = 400;
allBlocks();
}else if((millis()-startTime)/100 >= POINTS_SPEED3 && cactiSpeed > 300){
cactiSpeed = 300;
allBlocks();
}else if((millis()-startTime)/100 >= POINTS_SPEED4 && cactiSpeed > 200){
cactiSpeed = 200;
allBlocks();
}
}
void drawPlayer(int y, bool clean){
int comp[2] = {1,0};
if(clean){
lcd.setCursor(4,comp[y]);
lcd.print(" ");
}
lcd.setCursor(4,y);
lcd.write(byte(0));
}
void addCactus(){
for(int i = 0; i < 16; i++){
if(cacti[i] == -1){
if(i>0 && cacti[i-1] == 14 && cactiSpeed == 500) break;
cacti[i] = 15;
cactiCount++;
break;
}
}
}
void clearCacti(){
for(int i = 0; i < 16; i++) cacti[i] = -1;
cactiCount = 0;
}
void removeFirstCactus(){
for(int i = 0; i < 16; i++){
cacti[i] = cacti[i+1];
if(i >= cactiCount){
cacti[i] = -1;
break;
}
}
cactiCount--;
}
void drawCacti(){
bool moved = false;
for(int i = 0; i < cactiCount; i++){
if(millis()-lastDraw >= cactiSpeed){
cacti[i] = cacti[i] - 1;
moved = true;
}
if(cacti[i] == 4 && playerY == 1){
started = false;
gameOver();
break;
}
lcd.setCursor(cacti[i], 1);
if(cacti[i]>-1){
lcd.write(CACTUS_STYLE);
}else{
removeFirstCactus();
i--;
}
}
if(moved) lastDraw = millis();
}
void generateCactus(){
if(millis()-lastGenerated < CACTUS_GEN_TIME) return;
int chance = random(0,cactiSpeed);
if(chance < CACTUS_GEN_CHANCE_THRESHOLD){
addCactus();
lastGenerated = millis();
}
}
void setup() {
lcd.begin(16, 2);
lcd.createChar(0, dino);
lcd.createChar(1, cactus1);
lcd.createChar(2, cactus2);
lcd.createChar(3, cactus3);
lcd.createChar(4, block);
lcd.setCursor(5,0);
lcd.print("APERTE");
lcd.setCursor(5,1);
lcd.print("O PULO");
pinMode(JUMP, INPUT);
pinMode(BUZZER, OUTPUT);
randomSeed(analogRead(0));
clearCacti();
}
void loop() {
if(started){
lcd.clear();
checkSpeed();
if(millis()-jumpTime >= MAX_JUMP_TIME*0.1){
noTone(BUZZER);
}
if(digitalRead(JUMP)){
if(!jumping){
jumpTime = millis();
jumping = true;
drawPlayer(--playerY, true);
tone(BUZZER, 800);
}
if(jumping){
if(millis()-jumpTime > MAX_JUMP_TIME){
jumping = false;
drawPlayer(++playerY, true);
noTone(BUZZER);
}
}
}else{
if(jumping){
jumping = false;
drawPlayer(++playerY, true);
}
}
generateCactus();
drawPlayer(playerY, false);
drawCacti();
delay(150);
}else{
if(digitalRead(JUMP)){
started = true;
startTime = millis();
tone(BUZZER, 1440);
delay(200);
noTone(BUZZER);
}
}
}
Vamos falar sobre algumas funções primeiro e depois vamos explicar como tudo funciona.
O comando:
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);Define uma variável chamada 'lcd' como sendo um display LCD, e os parâmetros passados no seu construtor são, respectivamente, os pinos de RS, E, DB4, DB5, DB6 e DB7. Essa variável vai permitir que utilizemos funções como:
- lcd.begin(colunas, linhas) - que inicializa o display LCD definindo o seu tamanho.
- lcd.clear() - que limpa o display lcd
- lcd.createChar(num, data) - que registra um caractere customizado ao LCD
- lcd.setCursor(x,y) - que define o cursor na posição selecionada
- lcd.print(valor) - que imprime no LCD o valor passado na posição atual do cursor
- lcd.write(byte) - imprime no LCD um caractere customizado criado na posição atual do cursor
Inclusive, para esse projeto, foram utilizados caracteres customizados e eles poderão ser vistos mais pra frente.
Quanto ao Buzzer, os comandos utilizados são:
- tone(pino, frequencia) - que toca uma determinada frequência num pino específico
- noTone(pino) - que para de tocar o som no buzzer em determinado pino
Agora vamos falar um pouco sobre as funções que eu mesmo criei:
- clearPlayer() - limpa o caractere do jogador na posição dele
- gameOver() - limpa a tela e mostra o texto "Game Over" junto com a pontuação do jogador
- allBlocks() - define todos os caracteres do display LCD como um bloco preenchido
- checkSpeed() - verifica a pontuação atual do jogador para mudar ou não a velocidade do jogo
- drawPlayer(y, clean) - desenha o jogador na posição y e limpa ou não a posição anterior
- addCactus() - adiciona um novo cacto ao jogo
- clearCacti() - remove todos os cactos existentes
- removeFirstCactus() - remove apenas o primeiro cacto da lista
- drawCacti() - desenha todos os cactos presentes
- generateCactus() - gera um cacto aleatoriamente
Na função setup do Arduino, nós inicializamos o LCD, adicionamos os nossos caracteres customizados, que podem ser vistos na imagem abaixo, imprimimos uma tela inicial de jogo, definimos os pinos do botão e do buzzer como entrada e saída, respectivamente, definimos um valor de semente para o gerador aleatório de forma aleatória e limpamos nossa lista de cactos.
Adicionar imagens dos caracteres customizados
Na função loop, verificamos se o jogo foi iniciado e, caso tenho sido, a tela será limpa em cada iteração para depois ser realizado a escrita novamente e verifica-se quando se pressiona o botão de pulo e executa as funções e desenhos para o pulo.
Como vimos, temos 3 tipos de cactos. Então fica a pergunta, como utilizar eles?
No nosso setup, nós definimos os Dinossauro como o caractere customizado 0, Cacto 1 como 1, Cacto 2 como 2, Cacto 3 como 3 e o bloco cheio como 4. E para fazer o uso de diferentes modelos de cactos, podemos usar as constantes encontradas no começo do código.
#define JUMP 9
#define BUZZER 8
#define MAX_JUMP_TIME 1200
#define POINTS_SPEED2 400
#define POINTS_SPEED3 900
#define POINTS_SPEED4 1500
#define CACTUS_GEN_CHANCE_THRESHOLD 60
#define CACTUS_GEN_TIME 800
#define CACTUS_STYLE 3
Então o que faz cada coisa?
| Constante | Função |
|---|---|
| JUMP | Define em qual pino está ligado o botão de pulo |
| BUZZER | Define em qual pino está ligado o buzzer |
| MAX_JUMP_TIME | Define o tempo máximo de pulo, pois o jogador não pode ficar voando se deixar o botão pressionado |
| POINTS_SPEED2 | Define a quantidade de pontos necessários para iniciar a velocidade 2 |
| POINTS_SPEED3 | Define a quantidade de pontos necessários para iniciar a velocidade 3 |
| POINTS_SPEED4 | Define a quantidade de pontos necessários para iniciar a velocidade 4 |
| CACTUS_GEN_CHANCE_THRESHOLD | Define o limiar para a chance de geração de um novo cacto |
| CACTUS_GEN_TIME | Define o tempo mínimo a ser passado para gerar um novo cacto |
| CACTUS_STYLE | Define o estilo do cacto (Valores possíveis: 1, 2 ou 3) |
Então essas são as configurações do nosso jogo!
Abaixo vocês poderão encontrar o vídeo desse projeto no canal do Youtube. Se tiver alguma dúvida, pode perguntar! Até mais!








Comentários
Postar um comentário