/* Link do codigo fonte : https://github.com/bytesByHarsh/youtubeProjects/blob/master/Arduino/dino_game/dino_game.ino
*/
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/*BOTÃO 1(VERDE) ===> ESQUERDA
BOTÃO 2(AMARELO) ===> DIREITA
BOTÃO 3(VERMELHO) ===> BAIXO
BOTÃO 4(AZUL) ===> CIMA
BOTÃO 5(CINZA) ===> SELECT -> PRESSIONAR PARA(selecionar o abrco que vai ser movido)
-> PRESSIONAR LONGA POR 1 SEGUNDO PARA(DESEMBACAR O PASSAGEIRO)
*/
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// Pinos dos botões
#define BUTTON_SELECT 15
#define BUTTON_LEFT 16
#define BUTTON_RIGHT 17
#define BUTTON_DOWN 18
#define BUTTON_UP 19
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//informações dos barcos
struct Boat {
int x;
int y;
char letter;
int capacity;
int passengers;
bool active;
};
struct Character {
int x;
int y;
bool rescued;
};
struct ButtonState {
bool isPressed;
unsigned long pressTime;
};
// Bitmaps para os diferentes barcos
static const unsigned char PROGMEM boat1[] = {
0x00, 0xFF, 0xFF, 0x00,
0x01, 0xFF, 0xFF, 0x80,
0x03, 0xFF, 0xFF, 0xC0,
0x07, 0xFF, 0xFF, 0xE0,
0x07, 0xFF, 0xFF, 0xE0,
0x07, 0x00, 0x00, 0xE0,
0x07, 0xFF, 0xFF, 0xE0,
0x07, 0x00, 0x00, 0xE0,
0x07, 0xFF, 0xFF, 0xE0,
0x07, 0x00, 0x00, 0xE0,
0x07, 0xFF, 0xFF, 0xE0,
0x07, 0xFF, 0xFF, 0xE0,
0x03, 0xFF, 0xFF, 0xC0,
0x01, 0xFF, 0xFF, 0x80,
0x00, 0xFF, 0xFF, 0x00
};
// Barco 2 - Padrão de linhas verticais
static const unsigned char PROGMEM boat2[] = {
0x00, 0xFF, 0xFF, 0x00,
0x01, 0xFF, 0xFF, 0x80,
0x03, 0xFF, 0xFF, 0xC0,
0x07, 0xFF, 0xFF, 0xE0,
0x07, 0x55, 0x55, 0xE0,
0x07, 0x55, 0x55, 0xE0,
0x07, 0x55, 0x55, 0xE0,
0x07, 0x55, 0x55, 0xE0,
0x07, 0x55, 0x55, 0xE0,
0x07, 0x55, 0x55, 0xE0,
0x07, 0x55, 0x55, 0xE0,
0x07, 0xFF, 0xFF, 0xE0,
0x03, 0xFF, 0xFF, 0xC0,
0x01, 0xFF, 0xFF, 0x80,
0x00, 0xFF, 0xFF, 0x00
};
// Barco 3 - Padrão quadriculado
static const unsigned char PROGMEM boat3[] = {
0x00, 0xFF, 0xFF, 0x00,
0x01, 0xFF, 0xFF, 0x80,
0x03, 0xFF, 0xFF, 0xC0,
0x07, 0xFF, 0xFF, 0xE0,
0x07, 0x55, 0xAA, 0xE0,
0x07, 0xAA, 0x55, 0xE0,
0x07, 0x55, 0xAA, 0xE0,
0x07, 0xAA, 0x55, 0xE0,
0x07, 0x55, 0xAA, 0xE0,
0x07, 0xAA, 0x55, 0xE0,
0x07, 0x55, 0xAA, 0xE0,
0x07, 0xFF, 0xFF, 0xE0,
0x03, 0xFF, 0xFF, 0xC0,
0x01, 0xFF, 0xFF, 0x80,
0x00, 0xFF, 0xFF, 0x00
};
static const unsigned char PROGMEM person[] = {
0x1E, 0x00,
0x3F, 0x00,
0x3F, 0x00,
0x1E, 0x00,
0x0C, 0x00,
0x7F, 0x80,
0x7F, 0x80,
0x7F, 0x80,
0x7F, 0x80,
0x3F, 0x00,
0x3F, 0x00,
0x3F, 0x00,
0x3F, 0x00,
0x77, 0x80,
0x63, 0x80,
0x63, 0x80,
0x63, 0x80,
0x63, 0x80,
0x63, 0x80,
0x63, 0x80,
0x41, 0x00,
0x41, 0x00,
0xE3, 0x80
};
const unsigned char* boats[] = {boat1, boat2, boat3};
ButtonState selectButton = {false, 0};
// Variáveis do jogo
Boat gameBoats[3];
Character characters[4];
int selectedBoat = 0;
bool gameWon = false;
const int BOAT_WIDTH = 32;
const int BOAT_HEIGHT = 16;
const int PERSON_WIDTH = 9;
const int PERSON_HEIGHT = 15;
const int MOVE_SPEED = 2;
// Paredes do labirinto
bool walls[SCREEN_HEIGHT/8][SCREEN_WIDTH/8];
void setup() {
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_DOWN, INPUT_PULLUP);
pinMode(BUTTON_LEFT, INPUT_PULLUP);
pinMode(BUTTON_RIGHT, INPUT_PULLUP);
pinMode(BUTTON_SELECT, INPUT_PULLUP);
initializeGame();
showStartScreen();
}
void initializeGame() {
int centerY = (SCREEN_HEIGHT - BOAT_HEIGHT) / 2;
gameBoats[0] = {(SCREEN_WIDTH/2) - BOAT_WIDTH - 10, centerY, 'A', 1, 0, true};
gameBoats[2] = {(SCREEN_WIDTH/2) - (BOAT_WIDTH/2), centerY, 'C', 2, 0, false};
gameBoats[1] = {(SCREEN_WIDTH/2) + 10, centerY, 'B', 1, 0, false};
characters[0] = {5, 10, false};
characters[1] = {SCREEN_WIDTH - PERSON_WIDTH - 5, 10, false};
characters[2] = {5, SCREEN_HEIGHT - PERSON_HEIGHT - 5, false};
characters[3] = {SCREEN_WIDTH - PERSON_WIDTH - 5, SCREEN_HEIGHT - PERSON_HEIGHT - 5, false};
memset(walls, 0, sizeof(walls));
walls[4][8] = true;
walls[4][9] = true;
walls[4][10] = true;
walls[5][8] = true;
walls[6][8] = true;
}
bool checkRectangleCollision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
const int buffer = 5;
return (x1 < x2 + w2 - buffer &&
x1 + w1 - buffer > x2 &&
y1 < y2 + h2 - buffer &&
y1 + h1 - buffer > y2);
}
bool checkCircleCollision(int x1, int y1, int w1, int h1, int cx, int cy, int radius) {
int closestX = max(x1, min(cx, x1 + w1));
int closestY = max(y1, min(cy, y1 + h1));
int distanceX = cx - closestX;
int distanceY = cy - closestY;
const int buffer = 3;
return (distanceX * distanceX + distanceY * distanceY) <= ((radius - buffer) * (radius - buffer));
}
bool checkBoatCollisions(Boat& boat, int newX, int newY) {
const int numRectangles = 5;
const int rectangles[numRectangles][4] = {
{0, 30, 20, 8},
{105, 30, 20, 8},
{30, 58, 10, 5},
{90, 58, 10, 5},
{55, 15, 20, 5}
};
for (int i = 0; i < numRectangles; i++) {
if (checkRectangleCollision(newX, newY, BOAT_WIDTH, BOAT_HEIGHT,
rectangles[i][0], rectangles[i][1],
rectangles[i][2], rectangles[i][3])) {
return true;
}
}
if (checkCircleCollision(newX, newY, BOAT_WIDTH, BOAT_HEIGHT, 65, 55, 7)) {
return true;
}
const int boatBuffer = 8;
for (int i = 0; i < 3; i++) {
if (i != selectedBoat && gameBoats[i].active) {
if (abs(newX - gameBoats[i].x) < BOAT_WIDTH - boatBuffer &&
abs(newY - gameBoats[i].y) < BOAT_HEIGHT - boatBuffer) {
return true;
}
}
}
return false;
}
void loop() {
if (!gameWon) {
handleInput();
updateGame();
drawGame();
} else {
showWinScreen();
if (digitalRead(BUTTON_SELECT) == LOW) {
delay(200);
gameWon = false;
initializeGame();
}
}
delay(50);
}
void handleInput() {
// Gerencia o estado do botão SELECT
bool currentSelectState = digitalRead(BUTTON_SELECT) == LOW;
// Detecta quando o botão é pressionado pela primeira vez
if (currentSelectState && !selectButton.isPressed) {
selectButton.isPressed = true;
selectButton.pressTime = millis();
}
// Detecta quando o botão é solto
else if (!currentSelectState && selectButton.isPressed) {
unsigned long pressDuration = millis() - selectButton.pressTime;
if (pressDuration >= 2000) {
// Pressão longa - tenta desembarcar passageiro
if (gameBoats[selectedBoat].passengers > 0) {
dropOffPassenger();
}
} else {
// Pressão curta - troca de barco
do {
selectedBoat = (selectedBoat + 1) % 3;
} while (gameBoats[selectedBoat].passengers >= gameBoats[selectedBoat].capacity);
}
selectButton.isPressed = false;
}
// Movimento do barco selecionado
if (gameBoats[selectedBoat].passengers < gameBoats[selectedBoat].capacity) {
int newX = gameBoats[selectedBoat].x;
int newY = gameBoats[selectedBoat].y;
if (digitalRead(BUTTON_UP) == LOW) {
newY -= MOVE_SPEED;
}
if (digitalRead(BUTTON_DOWN) == LOW) {
newY += MOVE_SPEED;
}
if (digitalRead(BUTTON_LEFT) == LOW) {
newX -= MOVE_SPEED;
}
if (digitalRead(BUTTON_RIGHT) == LOW) {
newX += MOVE_SPEED;
}
newX = constrain(newX, 0, SCREEN_WIDTH - BOAT_WIDTH);
newY = constrain(newY, 0, SCREEN_HEIGHT - BOAT_HEIGHT);
if (!checkBoatCollisions(gameBoats[selectedBoat], newX, newY)) {
gameBoats[selectedBoat].x = newX;
gameBoats[selectedBoat].y = newY;
}
// Mantém dentro dos limites
gameBoats[selectedBoat].x = constrain(gameBoats[selectedBoat].x, 0, SCREEN_WIDTH - BOAT_WIDTH);
gameBoats[selectedBoat].y = constrain(gameBoats[selectedBoat].y, 0, SCREEN_HEIGHT - BOAT_HEIGHT);
}
}
void dropOffPassenger() {
// Procura o último passageiro resgatado por este barco
for (int i = 3; i >= 0; i--) { // Começa do último para pegar o mais recente
if (characters[i].rescued) {
// Coloca o passageiro próximo ao barco
characters[i].x = gameBoats[selectedBoat].x + BOAT_WIDTH + 2;
characters[i].y = gameBoats[selectedBoat].y;
// Ajusta para manter dentro da tela
characters[i].x = constrain(characters[i].x, 0, SCREEN_WIDTH - PERSON_WIDTH);
characters[i].y = constrain(characters[i].y, 0, SCREEN_HEIGHT - PERSON_HEIGHT);
characters[i].rescued = false;
gameBoats[selectedBoat].passengers--;
break;
}
}
}
void updateGame() {
// Verifica colisões com personagens
for (int i = 0; i < 4; i++) {
if (!characters[i].rescued) {
if (checkCollision(gameBoats[selectedBoat], characters[i]) &&
gameBoats[selectedBoat].passengers < gameBoats[selectedBoat].capacity) {
characters[i].rescued = true;
gameBoats[selectedBoat].passengers++;
}
}
}
// Verifica condição de vitória
gameWon = true;
for (int i = 0; i < 4; i++) {
if (!characters[i].rescued) {
gameWon = false;
break;
}
}
}
bool checkCollision(Boat boat, Character character) {
return (boat.x < character.x + PERSON_WIDTH &&
boat.x + BOAT_WIDTH > character.x &&
boat.y < character.y + PERSON_HEIGHT &&
boat.y + BOAT_HEIGHT > character.y);
}
void drawGame() {
display.clearDisplay();
// Desenha os indicadores de capacidade no topo
display.setTextSize(1);
display.setTextColor(WHITE);
// Posições para os indicadores
const int yPos = 0;
const int spacing = 42;
for (int i = 0; i < 3; i++) {
int xPos = i * spacing + 5;
display.setCursor(xPos, yPos);
display.print(gameBoats[i].letter);
display.print(gameBoats[i].passengers);
display.print('/');
display.print(gameBoats[i].capacity);
// Indica se o barco está selecionado
if (i == selectedBoat) {
display.drawLine(xPos, yPos + 8, xPos + 20, yPos + 8, WHITE);
}
}
// Desenha os personagens não resgatados
for (int i = 0; i < 4; i++) {
if (!characters[i].rescued) {
display.drawBitmap(characters[i].x, characters[i].y, person, PERSON_WIDTH, PERSON_HEIGHT, WHITE);
}
}
// Desenha os barcos
for (int i = 0; i < 3; i++) {
display.drawBitmap(
gameBoats[i].x,
gameBoats[i].y,
boats[i],
BOAT_WIDTH,
BOAT_HEIGHT,
(i == selectedBoat) ? WHITE : INVERSE
);
}
display.drawCircle(65, 55 ,7 , WHITE);
display.drawRect(0, 30, 20, 8, WHITE);
display.drawRect(105, 30, 20, 8, WHITE);
display.drawRect(30, 58, 10, 5, WHITE);
display.drawRect(90, 58, 10, 5, WHITE);
display.drawRect(55, 15, 20, 5, WHITE);
display.display();
}
void showStartScreen() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(15, 10);
display.println("TITANIC GAME LITE");
display.setCursor(10, 30);
display.println("SALVAR TODOS OS PASSAGEIROS");
display.setCursor(10, 45);
display.println("Presse SELECT para commecar");
display.display();
while(digitalRead(BUTTON_SELECT) == HIGH) {
delay(50);
}
delay(200);
}
void showWinScreen() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(20, 10);
display.println("VOCÊ GANHOU!");
display.setTextSize(1);
display.setCursor(10, 40);
display.println("TODOS OS PASSAGEIROS SALVOS!");
display.display();
}