#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define ANCHO_PANTALLA 128
#define ALTO_PANTALLA 64
#define moveRight 17 // conexión con la esp32 del botón derecho
#define moveLeft 2 // conexión con la esp32 del botón izquierdo
#define moveUp 5 // conexión con la esp32 del botón arriba
#define moveDown 18 // conexión con la esp32 del botón abajo
// Estructura para devolver las posiciones y velocidades de los fantasmas
struct Fantasma {
int x;
int y;
};
Fantasma fantasma1;
Fantasma fantasma2;
bool juegoPerdido = false;
int velFantasmaX = 1;
int velFantasmaY = 1;
int fantasmaX, fantasmaY;
int pacmanX = 0, pacmanY = 0, puntos = 0;;
int ultimaDireccion = -1; // 1: derecha, -1: izquierda, 2: arriba, -2:abajo
int pacmanAncho = 12;
int pacmanAlto = 16;
// Dimensiones de los puntos de comida (ajústalas si es necesario)
int comidaAncho = 1;
int comidaAlto = 1;
bool estadoActualD, estadoAnteriorD, estadoActualL, estadoAnteriorL;
bool estadoActualU, estadoAnteriorU, estadoActualR, estadoAnteriorR;
const int puntosComida[][2] = {
{15, 5}, {25, 5}, {35, 5}, {45, 5}, {55, 5}, {65, 5}, {75, 5}, {85, 5}, {95, 5}, {105, 5}, {115, 5}, {122, 5}, // Fila superior
{6, 12}, {25, 12}, {100, 12}, {122, 12},
{6, 18}, {15, 18}, {25, 18}, {45, 18}, {55, 18}, {65, 18}, {75, 18}, {85, 18}, {100, 18}, {112, 18}, {122, 18}, // Fila antes de la zona de fantasmas
{6, 30}, {15, 30}, {25, 30}, {35, 30}, {45, 30}, {85, 30}, {95, 30}, {105, 30}, {115, 30}, {122, 30}, // Fila inferior
{6, 42}, {15, 42}, {25, 42}, {45, 42}, {55, 42}, {65, 42}, {75, 42}, {85, 42}, {100, 42}, {112, 42}, {122, 42}, // Fila después de la zona de fantasmas
{6, 48}, {45, 48}, {85, 48}, {122, 48},
{6, 55}, {15, 55}, {25, 55}, {35, 55}, {45, 55}, {55, 55}, {75, 55}, {85, 55}, {95, 55}, {105, 55}, {115, 55}, {122, 55} // Fila inferior
};
const int numPuntosComida = sizeof(puntosComida) / sizeof(puntosComida[0]);
bool puntosRecogidos[numPuntosComida] = {false}; // Array que indica si un punto ha sido recogido
Adafruit_SSD1306 oled(ANCHO_PANTALLA, ALTO_PANTALLA, &Wire, -1);
static const unsigned char PROGMEM pacman_chiqui[] = {
0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x07, 0xe0, 0x0f, 0xe0, 0x0f, 0x80, 0x0f, 0x80, 0x0f, 0xe0,
0x07, 0xf0, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00
};
static const unsigned char PROGMEM pacman_izq[] {
0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x0f, 0xe0, 0x07, 0xf0, 0x01, 0xf0, 0x01, 0xf0, 0x07, 0xf0,
0x0f, 0xe0, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00
};
static const unsigned char PROGMEM pacman_arriba[] {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x39, 0xc0, 0x39, 0xc0, 0x3f, 0xc0,
0x3f, 0xc0, 0x3f, 0xc0, 0x1f, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const unsigned char PROGMEM pacman_abajo[] {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x80, 0x3f, 0xc0, 0x3f, 0xc0,
0x39, 0xc0, 0x39, 0xc0, 0x19, 0x80, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const unsigned char PROGMEM fantasma[] = {
0x00, 0x00, 0x01, 0xc0, 0x07, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x0c, 0x40, 0x1f, 0xf8, 0x1f, 0xf8,
0x1f, 0xf8, 0x1f, 0x78, 0x12, 0x48, 0x00, 0x00
};
const int muros[][4] = {
// Borde superior
{0, 0, 128, 0}, // Parte superior
//Borde inferior
{0, 64, 128, 64}, // Parte inferior
// Borde izquierdo con abertura
{0, 0, 0, 24}, // Parte superior izquierda
{0, 40, 0, 64}, // Parte inferior izquierda
{0, 24, 18, 24}, // Abertura arriba
{0, 36, 18, 36}, // Abertura abajo
// Borde derecho con abertura
{130, 0, 130, 24}, // Parte superior derecha
{130, 40, 130, 64}, // Parte inferior derecha
{116, 24, 130, 24}, // Abertura arriba
{116, 36, 130, 36}, // Abertura abajo
// linea de esquina superior izquierda
{17, 12, 18, 12},
// linea de esquina superior derecha
{112, 12, 114, 12},
// Cuadro central (donde salen fantasmas)
{63, 35, 74, 35},
{63, 35, 63, 25},
{74, 35, 74, 25},
{63, 25, 74, 25}, // aqui se hizo modificación para que el pacman no pueda entrar a lo de fantasmas
// L izquierda superior
{45, 12, 53, 12}, // Horizontal
{35, 12, 35, 20}, // Vertical
// L derecha superiorS
{77, 12, 94, 12}, // Horizontal
{94, 12, 94, 20}, // Vertical
// L izquierda inferior
{35, 40, 35, 48}, // Horizontal
{17, 48, 35, 48}, // Vertical
// L derecha inferior
{94, 40, 94, 48}, // vertical
{94, 48, 114, 48}, // horizontal
// T inferior vertical
{60, 48, 74, 48}, // Horizontal
{64, 48, 64, 64}, // Vertical
};
void setup() {
Serial.begin(115200);
Serial.println("Hello, ESP32!");
pinMode(moveRight, INPUT);
pinMode(moveLeft, INPUT);
pinMode(moveUp, INPUT);
pinMode(moveDown, INPUT);
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(("Fallo la conexión con la pantalla..."));
while (1);
}
Serial.println(("Pantalla conectada al sistema"));
setup_oled();
inicializar();
}
void inicializar() {
for (int i = 0; i < numPuntosComida; i++) {
puntosRecogidos[i] = false; // Reinicia todos los puntos a no recogidos
}
// Reinicia otras variables de estado del juego si es necesario
juegoPerdido = false;
puntos = 0;
pacmanX = 0;
pacmanY = 0;
fantasma2.x = 115;
fantasma2.y = 40;
fantasma1.x = 115;
fantasma1.y = 0;
oled.clearDisplay();
oled.drawBitmap(pacmanX, pacmanY, pacman_chiqui, 16, 12, WHITE);
oled.display();
}
void setup_oled() {
delay(2000);
oled.clearDisplay();
oled.setTextSize(1); // Medida texto
oled.setTextColor(WHITE);
oled.setCursor(30, 30); // Posición(x,y) -> las posiciones de y estan invertidas
oled.println("PACMAN ");
oled.display();
delay(2000);
}
// ---------------------------------------------------------------------
// -------------------- FUNCIONES PARA PACMAN --------------------------
// Función para mover el Pacman
void mover_pacman() {
estadoActualR = digitalRead(moveRight);
if (estadoActualR && !estadoAnteriorR) {
int nuevoX = pacmanX + 6;
if (!colision(nuevoX, pacmanY)) {
if (nuevoX > ANCHO_PANTALLA) {
pacmanX = 0;
} else {
pacmanX = nuevoX;
}
}
ultimaDireccion = 1; // Se movió hacia la derecha
}
estadoAnteriorR = estadoActualR;
estadoActualL = digitalRead(moveLeft);
if (estadoActualL && !estadoAnteriorL) {
int nuevoX = pacmanX - 6;
if (!colision(nuevoX, pacmanY)) {
if (nuevoX < 0) {
pacmanX = 126;
} else {
pacmanX = nuevoX;
}
}
ultimaDireccion = -1; // Se movió hacia la izquierda
}
estadoAnteriorL = estadoActualL;
// Mover hacia arriba
estadoActualU = digitalRead(moveUp);
if (estadoActualU && !estadoAnteriorU) {
int nuevoY = pacmanY - 6; // Velocidad de 6 hacia arriba
if (!colision(pacmanX, nuevoY)) {
pacmanY = nuevoY;
}
ultimaDireccion = 2;
}
estadoAnteriorU = estadoActualU;
// Mover hacia abajo
estadoActualD = digitalRead(moveDown);
if (estadoActualD && !estadoAnteriorD) {
int nuevoY = pacmanY + 6; // Velocidad de 6 hacia abajo
if (!colision(pacmanX, nuevoY)) {
pacmanY = nuevoY;
}
ultimaDireccion = -2;
}
estadoAnteriorD = estadoActualD;
verificarColisionComida(); // Verifica si Pacman ha recogido un punto de comida
verificarColisionFantasma();
}
// ------------------- FUNCIONES PARA FANTASMAS ------------------------
Fantasma mover_fantasma(int fantasmaX, int fantasmaY, int velFantasmaX, int velFantasmaY) {
// Calcula la diferencia entre las posiciones de Pacman y el fantasma
int dx = pacmanX - fantasmaX;
int dy = pacmanY - fantasmaY;
bool movido = false;
// Intentar moverse hacia Pacman en el eje X primero
if (abs(dx) > abs(dy)) {
// Pacman está a la derecha
if (dx > 0 && !colisionF(fantasmaX + velFantasmaX, fantasmaY)) {
// Mover hacia la derecha
fantasmaX += velFantasmaX;
movido = true;
}
// Pacman está a la izquierda
else if (dx < 0 && !colisionF(fantasmaX - velFantasmaX, fantasmaY)) {
// Mover hacia la izquierda
fantasmaX -= velFantasmaX;
movido = true;
}
}
// Si no se pudo mover en el eje X, intentar en el eje Y
if (!movido) {
if (dy > 0 && !colisionF(fantasmaX, fantasmaY + velFantasmaY)) {
// Mover hacia abajo
fantasmaY += velFantasmaY;
movido = true;
} else if (dy < 0 && !colisionF(fantasmaX, fantasmaY - velFantasmaY)) {
// Mover hacia arriba
fantasmaY -= velFantasmaY;
movido = true;
}
}
// Si no se pudo mover ni en el eje X ni en el eje Y, intentar moverse en una dirección perpendicular
if (!movido) {
if (abs(dx) > abs(dy)) {
// Si no pudo moverse en el eje X, intentar moverse en el eje Y (perpendicular)
if (dy > 0 && !colisionF(fantasmaX, fantasmaY + velFantasmaY)) {
fantasmaY += velFantasmaY;
} else if (dy < 0 && !colisionF(fantasmaX, fantasmaY - velFantasmaY)) {
fantasmaY -= velFantasmaY;
}
} else {
// Si no pudo moverse en el eje Y, intentar moverse en el eje X (perpendicular)
if (dx > 0 && !colisionF(fantasmaX + velFantasmaX, fantasmaY)) {
fantasmaX += velFantasmaX;
} else if (dx < 0 && !colisionF(fantasmaX - velFantasmaX, fantasmaY)) {
fantasmaX -= velFantasmaX;
}
}
}
// Si no se pudo mover en ninguna dirección, elegir un movimiento aleatorio para evitar estancarse
if (!movido) {
int direccion = rand() % 4;
switch (direccion) {
case 0: // Intentar mover a la derecha
if (!colisionF(fantasmaX + velFantasmaX, fantasmaY)) {
fantasmaX += velFantasmaX;
}
break;
case 1: // Intentar mover a la izquierda
if (!colisionF(fantasmaX - velFantasmaX, fantasmaY)) {
fantasmaX -= velFantasmaX;
}
break;
case 2: // Intentar mover hacia abajo
if (!colisionF(fantasmaX, fantasmaY + velFantasmaY)) {
fantasmaY += velFantasmaY;
}
break;
case 3: // Intentar mover hacia arriba
if (!colisionF(fantasmaX, fantasmaY - velFantasmaY)) {
fantasmaY -= velFantasmaY;
}
break;
}
}
// Retornar la nueva posición del fantasma
Fantasma resultado;
resultado.x = fantasmaX;
resultado.y = fantasmaY;
return resultado;
}
// ------------------- FUNCIONES PARA LABERINTO -----------------------
void generar_laberinto() {
// Borde superior
oled.drawLine(0, 0, 128, 0, SSD1306_WHITE); // Parte superior
// Borde izquierdo con abertura
oled.drawLine(0, 0, 0, 24, SSD1306_WHITE); // Parte superior izquierda
oled.drawLine(0, 36, 0, 64, SSD1306_WHITE); // Parte inferior izquierda
oled.drawLine(0, 24, 18, 24, SSD1306_WHITE); // abertura arriba
oled.drawLine(0, 36, 18, 36, SSD1306_WHITE); // abertura abajo
// Borde inferior
oled.drawLine(0, 63, 128, 63, SSD1306_WHITE); // Parte inferior
// Borde derecho con abertura
oled.drawLine(127, 0, 127, 24, SSD1306_WHITE); // Parte superior derecha
oled.drawLine(127, 36, 127, 64, SSD1306_WHITE); // Parte inferior derecha
oled.drawLine(127, 24, 108, 24, SSD1306_WHITE); // abertura arriba
oled.drawLine(127, 36, 108, 36, SSD1306_WHITE); // abertura abajo
// linea de esquina superior izquierda
oled.drawLine(13, 12, 19, 12, SSD1306_WHITE);
// linea de esquina superior derecha
oled.drawLine(114, 12, 108, 12, SSD1306_WHITE);
// Cuadro central (donde salen fantasmas)
oled.drawLine(54, 35, 74, 35, SSD1306_WHITE); // Línea horizontal inferior
oled.drawLine(54, 35, 54, 25, SSD1306_WHITE); // Línea vertical izquierda
oled.drawLine(74, 35, 74, 25, SSD1306_WHITE); // Línea vertical derecha
oled.drawLine(54, 25, 59, 25, SSD1306_WHITE); // Línea horizontal superior izquierda
oled.drawLine(69, 25, 74, 25, SSD1306_WHITE); // Línea horizontal superior derecha
// L izquierda superior
oled.drawLine(35, 12, 57, 12, SSD1306_WHITE); // Horizontal
oled.drawLine(35, 12, 35, 24, SSD1306_WHITE); // Vertical
// L derecha superior
oled.drawLine(71, 12, 93, 12, SSD1306_WHITE); // Horizontal
oled.drawLine(93, 12, 93, 24, SSD1306_WHITE); // Vertical
// L izquierda inferior
oled.drawLine(35, 36, 35, 48, SSD1306_WHITE); // Vertical
oled.drawLine(13, 48, 35, 48, SSD1306_WHITE); //Horizontal
// L derecha inferior
oled.drawLine(93, 36, 93, 48, SSD1306_WHITE); // Vertical
oled.drawLine(114, 48, 93, 48, SSD1306_WHITE); // Horizontal
// T inferior vertical
oled.drawLine(54, 46, 74, 46, SSD1306_WHITE); //linea horizontal
oled.drawLine(64, 46, 64, 64, SSD1306_WHITE); //linea vertical
}
bool colision(int nuevoX, int nuevoY) {
// Verificar colisión con los muros
for (int i = 0; i < sizeof(muros) / sizeof(muros[0]); i++) {
int x1 = muros[i][0];
int y1 = muros[i][1];
int x2 = muros[i][2];
int y2 = muros[i][3];
// Comprobación de colisión simple
if (nuevoX < x2 && nuevoX + 16 > x1 && nuevoY < y2 && nuevoY + 12 > y1) {
Serial.println("X1: " + String(x1) + " Y1: " + String(y1) + "X2: " + String(x2) + " Y2: " + String(y2));
return true; // Colisión detectada
}
}
return false; // Sin colisión
}
bool colisionF(int nuevoX, int nuevoY) {
// Verificar colisión con los muros
for (int i = 0; i < sizeof(muros) / sizeof(muros[0]); i++) {
int x1 = muros[i][0];
int y1 = muros[i][1];
int x2 = muros[i][2];
int y2 = muros[i][3];
// Comprobación de colisión simple
if (nuevoX < x2 && nuevoX + 16 > x1 && nuevoY < y2 && nuevoY + 12 > y1) {
//Serial.println("X1: " + String(x1) + " Y1: " + String(y1) + "X2: " + String(x2) + " Y2: " + String(y2));
return true; // Colisión detectada
}
}
return false; // Sin colisión
}
// ------------------- FUNCIONES PARA COMIDA --------------------------
void dibujarPuntosComida() {
for (int i = 0; i < numPuntosComida; i++) {
if (!puntosRecogidos[i]) { // Solo dibuja los puntos no recogidos
oled.drawPixel(puntosComida[i][0], puntosComida[i][1], WHITE);
}
}
}
void verificarColisionComida() {
for (int i = 0; i < numPuntosComida; i++) {
if (!puntosRecogidos[i]) { // Si el punto no ha sido recogido aún
// Posición del punto de comida
int comidaX = puntosComida[i][0];
int comidaY = puntosComida[i][1];
// Comprobamos si el rectángulo de Pacman intersecta con el rectángulo del punto de comida
bool colision = (pacmanX < comidaX + comidaAncho &&
pacmanX + pacmanAncho > comidaX &&
pacmanY < comidaY + comidaAlto &&
pacmanY + pacmanAlto > comidaY);
if (colision) {
puntosRecogidos[i] = true; // Marca el punto como recogido
puntos += 1; // Aumenta el puntaje
Serial.println("Puntos: " + String(puntos)); // Muestra el puntaje en el monitor serial
}
}
}
}
// Función para verificar si Pacman se choca con fantasma
// Función para verificar colisión entre Pacman y fantasmas
void verificarColisionFantasma() {
const int rangoColision = 5;
// Comprobar colisión con el primer fantasma
if (pacmanX >= (fantasma1.x - rangoColision) && pacmanX <= (fantasma1.x + rangoColision) &&
pacmanY >= (fantasma1.y - rangoColision) && pacmanY <= (fantasma1.y + rangoColision)) {
Serial.println("juego perdido ");
juegoPerdido = true;
}
// Comprobar colisión con el segundo fantasma
if (pacmanX >= (fantasma2.x - rangoColision) && pacmanX <= (fantasma2.x + rangoColision) &&
pacmanY >= (fantasma2.y - rangoColision) && pacmanY <= (fantasma2.y + rangoColision)) {
Serial.println("juego perdido ");
juegoPerdido = true;
}
}
// ------------------- FUNCIONES TÉCNICAS -----------------------------
// Dibujar ambos objetos
void dibujar_escena() {
oled.clearDisplay();
generar_laberinto();
dibujarPuntosComida();
// Dibujar el fantasma
oled.drawBitmap(fantasma1.x, fantasma1.y, fantasma, 16, 12, WHITE);
oled.drawBitmap(fantasma2.x, fantasma2.y, fantasma, 16, 12, WHITE);
// Dibujar Pacman dependiendo de la última dirección
if (ultimaDireccion == -1) {
oled.drawBitmap(pacmanX, pacmanY, pacman_izq, 16, 12, WHITE);
} else if (ultimaDireccion == 1) {
oled.drawBitmap(pacmanX, pacmanY, pacman_chiqui, 16, 12, WHITE);
} else if (ultimaDireccion == 2) {
oled.drawBitmap(pacmanX, pacmanY, pacman_arriba, 16, 12, WHITE);
} else {
oled.drawBitmap(pacmanX, pacmanY, pacman_abajo, 16, 12, WHITE);
}
oled.display();
}
void inicializar_juego() {
mover_pacman();
fantasma1 = mover_fantasma(fantasma1.x, fantasma1.y, velFantasmaX, velFantasmaY);
fantasma2 = mover_fantasma(fantasma2.x, fantasma2.y, velFantasmaX, velFantasmaY);
dibujar_escena();
delay(50);
}
void loop() {
if (puntos == 64) {
oled.clearDisplay(); // Limpia la pantalla
oled.setCursor(30, 15);
oled.setTextSize(1);
oled.println("¡Ganaste!");
oled.setTextSize(1);
oled.print("Total de puntos: ");
oled.println(puntos);
oled.display();
delay(3000);
inicializar();
inicializar_juego();
} else if (juegoPerdido) {
oled.clearDisplay();
oled.setCursor(30, 15);
oled.setTextSize(1);
oled.println("¡Perdiste!");
oled.setTextSize(1);
oled.print("Total de puntos: ");
oled.println(puntos);
oled.display();
delay(3000);
inicializar();
inicializar_juego();
} else {
inicializar_juego();
}
}