Increíble Juego de Corredor con Arduino

Crea tu Propio Juego de Corredor con Arduino: ¡Esquiva Obstáculos y Gana Puntos!

Si eres un entusiasta de la electrónica y la programación, este proyecto es perfecto para ti. Hoy aprenderás a crear un emocionante juego de corredor con Arduino, donde tu objetivo será esquivar obstáculos y acumular la mayor cantidad de puntos posible. Utilizando una pantalla LCD, un módulo I2C, una protoboard y un interruptor, construirás un juego simple pero entretenido que te permitirá mejorar tus habilidades con Arduino.

Materiales Necesarios

Conexión del Hardware

Conexión para juego con Arduino

Para empezar, realiza las siguientes conexiones:

  • Pantalla LCD: Conecta la pantalla al Arduino utilizando el módulo I2C. Esto simplifica las conexiones y el código, permitiéndote controlar la pantalla con solo dos pines.
  • Interruptor: Conecta el interruptor a un pin digital del Arduino (por ejemplo, el pin 2) y a tierra. Este será el control para hacer que el corredor salte.

Código Fuente del Juego

Este juego de corredor presenta un personaje que corre y debe saltar para esquivar los obstáculos que aparecen desde la derecha de la pantalla. A medida que el personaje avanza sin chocar, acumula puntos.

#include <LiquidCrystal.h>

#define PIN_BUTTON 2
#define PIN_AUTOPLAY 1
#define PIN_READWRITE 10
#define PIN_CONTRAST 12

#define SPRITE_RUN1 1
#define SPRITE_RUN2 2
#define SPRITE_JUMP 3
#define SPRITE_JUMP_UPPER '.'         // Usa el carácter '.' para la cabeza
#define SPRITE_JUMP_LOWER 4
#define SPRITE_TERRAIN_EMPTY ' '      // Usa el carácter ' ' para vacío
#define SPRITE_TERRAIN_SOLID 5
#define SPRITE_TERRAIN_SOLID_RIGHT 6
#define SPRITE_TERRAIN_SOLID_LEFT 7

#define HERO_HORIZONTAL_POSITION 1    // Posición horizontal del héroe en la pantalla

#define TERRAIN_WIDTH 16
#define TERRAIN_EMPTY 0
#define TERRAIN_LOWER_BLOCK 1
#define TERRAIN_UPPER_BLOCK 2

#define HERO_POSITION_OFF 0          // El héroe es invisible
#define HERO_POSITION_RUN_LOWER_1 1  // El héroe está corriendo en la fila inferior (pose 1)
#define HERO_POSITION_RUN_LOWER_2 2  //                               (pose 2)

#define HERO_POSITION_JUMP_1 3       // Comenzando un salto
#define HERO_POSITION_JUMP_2 4       // A mitad de camino hacia arriba
#define HERO_POSITION_JUMP_3 5       // El salto está en la fila superior
#define HERO_POSITION_JUMP_4 6       // El salto está en la fila superior
#define HERO_POSITION_JUMP_5 7       // El salto está en la fila superior
#define HERO_POSITION_JUMP_6 8       // El salto está en la fila superior
#define HERO_POSITION_JUMP_7 9       // A mitad de camino hacia abajo
#define HERO_POSITION_JUMP_8 10      // A punto de aterrizar

#define HERO_POSITION_RUN_UPPER_1 11 // El héroe está corriendo en la fila superior (pose 1)
#define HERO_POSITION_RUN_UPPER_2 12 //                               (pose 2)

LiquidCrystal lcd(11, 9, 6, 5, 4, 3);
static char terrainUpper[TERRAIN_WIDTH + 1];
static char terrainLower[TERRAIN_WIDTH + 1];
static bool buttonPushed = false;

void initializeGraphics(){
  static byte graphics[] = {
    // Posición de carrera 1
    B01100,
    B01100,
    B00000,
    B01110,
    B11100,
    B01100,
    B11010,
    B10011,
    // Posición de carrera 2
    B01100,
    B01100,
    B00000,
    B01100,
    B01100,
    B01100,
    B01100,
    B01110,
    // Salto
    B01100,
    B01100,
    B00000,
    B11110,
    B01101,
    B11111,
    B10000,
    B00000,
    // Salto inferior
    B11110,
    B01101,
    B11111,
    B10000,
    B00000,
    B00000,
    B00000,
    B00000,
    // Suelo
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    B11111,
    // Suelo derecha
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    B00011,
    // Suelo izquierda
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
    B11000,
  };
  int i;
  // Omitir el uso del carácter 0, esto permite que lcd.print() se use para
  // dibujar rápidamente varios caracteres
  for (i = 0; i < 7; ++i) {
	  lcd.createChar(i + 1, &graphics[i * 8]);
  }
  for (i = 0; i < TERRAIN_WIDTH; ++i) {
    terrainUpper[i] = SPRITE_TERRAIN_EMPTY;
    terrainLower[i] = SPRITE_TERRAIN_EMPTY;
  }
}

// Desplazar el terreno a la izquierda en incrementos de medio carácter
//
void advanceTerrain(char* terrain, byte newTerrain){
  for (int i = 0; i < TERRAIN_WIDTH; ++i) {
    char current = terrain[i];
    char next = (i == TERRAIN_WIDTH-1) ? newTerrain : terrain[i+1];
    switch (current){
      case SPRITE_TERRAIN_EMPTY:
        terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY;
        break;
      case SPRITE_TERRAIN_SOLID:
        terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID;
        break;
      case SPRITE_TERRAIN_SOLID_RIGHT:
        terrain[i] = SPRITE_TERRAIN_SOLID;
        break;
      case SPRITE_TERRAIN_SOLID_LEFT:
        terrain[i] = SPRITE_TERRAIN_EMPTY;
        break;
    }
  }
}

bool drawHero(byte position, char* terrainUpper, char* terrainLower, unsigned int score) {
  bool collide = false;
  char upperSave = terrainUpper[HERO_HORIZONTAL_POSITION];
  char lowerSave = terrainLower[HERO_HORIZONTAL_POSITION];
  byte upper, lower;
  switch (position) {
    case HERO_POSITION_OFF:
      upper = lower = SPRITE_TERRAIN_EMPTY;
      break;
    case HERO_POSITION_RUN_LOWER_1:
      upper = SPRITE_TERRAIN_EMPTY;
      lower = SPRITE_RUN1;
      break;
    case HERO_POSITION_RUN_LOWER_2:
      upper = SPRITE_TERRAIN_EMPTY;
      lower = SPRITE_RUN2;
      break;
    case HERO_POSITION_JUMP_1:
    case HERO_POSITION_JUMP_8:
      upper = SPRITE_TERRAIN_EMPTY;
      lower = SPRITE_JUMP;
      break;
    case HERO_POSITION_JUMP_2:
    case HERO_POSITION_JUMP_7:
      upper = SPRITE_JUMP_UPPER;
      lower = SPRITE_JUMP_LOWER;
      break;
    case HERO_POSITION_JUMP_3:
    case HERO_POSITION_JUMP_4:
    case HERO_POSITION_JUMP_5:
    case HERO_POSITION_JUMP_6:
      upper = SPRITE_JUMP;
      lower = SPRITE_TERRAIN_EMPTY;
      break;
    case HERO_POSITION_RUN_UPPER_1:
      upper = SPRITE_RUN1;
      lower = SPRITE_TERRAIN_EMPTY;
      break;
    case HERO_POSITION_RUN_UPPER_2:
      upper = SPRITE_RUN2;
      lower = SPRITE_TERRAIN_EMPTY;
      break;
  }
  if (upper != ' ') {
    terrainUpper[HERO_HORIZONTAL_POSITION] = upper;
    collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true;
  }
  if (lower != ' ') {
    terrainLower[HERO_HORIZONTAL_POSITION] = lower;
    collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true;
  }
  
  byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1;
  
  // Dibujar la escena
  terrainUpper[TERRAIN_WIDTH] = '\0';
  terrainLower[TERRAIN_WIDTH] = '\0';
  char temp = terrainUpper[16-digits];
  terrainUpper[16-digits] = '\0';
  lcd.setCursor(0,0);
  lcd.print(terrainUpper);
  terrainUpper[16-digits] = temp;  
  lcd.setCursor(0,1);
  lcd.print(terrainLower);
  
  lcd.setCursor(16 - digits,0);
  lcd.print(score);

  terrainUpper[HERO_HORIZONTAL_POSITION] = upperSave;
  terrainLower[HERO_HORIZONTAL_POSITION] = lowerSave;
  return collide;
}

// Manejar la pulsación del botón como una interrupción
void buttonPush() {
  buttonPushed = true;
}

void setup(){
  pinMode(PIN_READWRITE, OUTPUT);
  digitalWrite(PIN_READWRITE, LOW);
  pinMode(PIN_CONTRAST, OUTPUT);
  digitalWrite(PIN_CONTRAST, LOW);
  pinMode(PIN_BUTTON, INPUT);
  digitalWrite(PIN_BUTTON, HIGH);
  pinMode(PIN_AUTOPLAY, OUTPUT);
  digitalWrite(PIN_AUTOPLAY, HIGH);
  
  // El pin digital 2 se mapea a la interrupción 0
  attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING);
  
  initializeGraphics();
  
  lcd.begin(16, 2);
}

void loop(){
  static byte heroPos = HERO_POSITION_RUN_LOWER_1;
  static byte newTerrainType = TERRAIN_EMPTY;
  static byte newTerrainDuration = 1;
  static bool playing = false;
  static bool blink = false;
  static unsigned int distance = 0;
  
  if (!playing) {
    drawHero((blink) ? HERO_POSITION_OFF : heroPos, terrainUpper, terrainLower, distance >> 3);
    if (blink) {
      lcd.setCursor(0,0);
      lcd.print("Presiona Start");
    }
    delay(250);
    blink = !blink;
    if (buttonPushed) {
      initializeGraphics();
      heroPos = HERO_POSITION_RUN_LOWER_1;
      playing = true;
      buttonPushed = false;
      distance = 0;
    }
    return;
  }

  // Desplazar el terreno a la izquierda
  advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
  advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
  
  // Crear nuevo terreno para ingresar por la derecha
  if (--newTerrainDuration == 0) {
    if (newTerrainType == TERRAIN_EMPTY) {
      newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK;
      newTerrainDuration = 2 + random(10);
    } else {
      newTerrainType = TERRAIN_EMPTY;
      newTerrainDuration = 10 + random(10);
    }
  }
    
  if (buttonPushed) {
    if (heroPos <= HERO_POSITION_RUN_LOWER_2) heroPos = HERO_POSITION_JUMP_1;
    buttonPushed = false;
  }  

  if (drawHero(heroPos, terrainUpper, terrainLower, distance >> 3)) {
    playing = false; // El héroe chocó con algo. Qué mal.
  } else {
    if (heroPos == HERO_POSITION_RUN_LOWER_2 || heroPos == HERO_POSITION_JUMP_8) {
      heroPos = HERO_POSITION_RUN_LOWER_1;
    } else if ((heroPos >= HERO_POSITION_JUMP_3 && heroPos <= HERO_POSITION_JUMP_5) && terrainLower[HERO_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) {
      heroPos = HERO_POSITION_RUN_UPPER_1;
    } else if (heroPos >= HERO_POSITION_RUN_UPPER_1 && terrainLower[HERO_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) {
      heroPos = HERO_POSITION_JUMP_5;
    } else if (heroPos == HERO_POSITION_RUN_UPPER_2) {
      heroPos = HERO_POSITION_RUN_UPPER_1;
    } else {
      ++heroPos;
    }
    ++distance;
    
    digitalWrite(PIN_AUTOPLAY, terrainLower[HERO_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW);
  }
  delay(50);
}

Este código establece un juego en el que un corredor debe esquivar obstáculos para ganar puntos. El juego se vuelve más desafiante a medida que avanza, ya que los obstáculos aparecen con mayor frecuencia.

Personalización del Juego

Puedes ajustar varios aspectos del juego para personalizarlo a tu gusto. Por ejemplo:

  • Velocidad del Juego: Modifica el valor en delay(50) para que el juego sea más rápido o más lento.
  • Dificultad: Cambia la frecuencia con la que aparecen los obstáculos o su tamaño para hacer el juego más difícil.

Prueba y Mejora

Una vez que hayas cargado el código en tu Arduino, juega y observa cómo responde el juego. Si encuentras que es demasiado fácil o difícil, ajusta las configuraciones hasta que obtengas el desafío perfecto.

Conclusión

Este juego de corredor es una excelente manera de practicar tus habilidades con Arduino y de aprender sobre el uso de pantallas LCD y módulos I2C. Además, es un proyecto divertido que puedes seguir expandiendo con nuevas características como niveles de dificultad o diferentes tipos de obstáculos. ¡Desafía a tus amigos y ve quién puede obtener la mayor puntuación!

Si estás en México y necesitas componentes electrónicos, te invitamos a visitar nuestra tienda de electrónica en la CDMX. ¡Te esperamos con todo lo que necesitas para tus proyectos!

Video juego con arduino corredor

Simulación con Tinkercad

Related Posts

¿Qué Son las Compuertas Lógicas? La Base de la Electrónica Digital

Aprende los distintos tipos de compuertas lógicas y domina la innovación en cada circuito. Con técnicas avanzadas impulsa tu creatividad y optimiza soluciones en electrónica digital.

🔌 Aprende a Medir Corriente con el Sensor ZMCT103C y Arduino 💡

Aprende a medir corriente alterna con precisión usando el sensor ZMCT103C y Arduino. Guía completa con materiales, código y ventajas del sensor

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

You Missed

¿Qué Son las Compuertas Lógicas? La Base de la Electrónica Digital

¿Qué Son las Compuertas Lógicas? La Base de la Electrónica Digital

¡Controla Arduino desde tu PC con HTML y JavaScript! 🚀

¡Controla Arduino desde tu PC con HTML y JavaScript! 🚀

Transforma Tu Proyecto con el Sensor de Voltaje 0-25V para Arduino 🚀🔋

Transforma Tu Proyecto con el Sensor de Voltaje 0-25V para Arduino 🚀🔋

Guía Definitiva: Aprende a Usar un Termopar Tipo K con Módulo MAX6675

Guía Definitiva: Aprende a Usar un Termopar Tipo K con Módulo MAX6675

🛠️ Todo sobre Resistencias: Tipos, Usos y Cómo Leerlas Eficazmente ⚡

🛠️ Todo sobre Resistencias: Tipos, Usos y Cómo Leerlas Eficazmente ⚡

¡Domina el Divisor de Tensión! ⚡️ Aprende a Calcularlo Fácilmente

¡Domina el Divisor de Tensión! ⚡️ Aprende a Calcularlo Fácilmente