Juego láser-tag Básico

¿Estáis preparadas para dejar lista una versión básica del juego?

Ayer nos quedamos con la ampliación que proponía el reto diario: Hacer funcionar 4 LEDs y 4 fotoresistencias a la vez.

Solución del RETO 2.1:

//Inicializamos los LEDs en los pines digitales 7, 8, 12, 13.
const int led0 = 7;
const int led1 = 8;
const int led2 = 12;
const int led3 = 13;
//Inicializamos las fotoresistencia en los pines analógicos A0, A1, A2, A3.
const int foto0 = A0;
const int foto1 = A1;
const int foto2 = A2;
const int foto3 = A3;
//Creamos una variable límite que es la que va decidir cual es el límite entre estar recibiendo luz o no. El valor del límite depende del la luz que haya en el ambiente, así que tendremos que hacer pruebas.
const int limite = 100;
//Inicializamos el zumbador en el pin digital con pwm (~) 9.
cont int zumb = 9;
 
// Recordatorio: setup() solo lo va a leer el arduino cuando se encienda, nunca más.
void setup() {
  Serial.begin(9600);   
  // Definimos los pines de los LEDs como pines de salida (OUTPUT), porque les vamos a mandar la información de que se enciendan o se apaguen.
  pinMode(led0 , OUTPUT);
  pinMode(led1 , OUTPUT);
  pinMode(led2 , OUTPUT);
  pinMode(led3 , OUTPUT);    
  // Definimos los pines de las fotoresistencias como pines de entrada (INPUT), porque les van a mandar información sobre como se encuentran al arduino.
  pinMode(foto0 , INPUT);   
  pinMode(foto1 , INPUT);
  pinMode(foto2 , INPUT);
  pinMode(foto3 , INPUT);
  //Encendemos los LEDs.  
  digitalWrite(led0,HIGH);
  digitalWrite(led1,HIGH);
  digitalWrite(led2,HIGH);
  digitalWrite(led3,HIGH);
}

// Recordatorio: loop() lo lee el arduino en bucle hasta que se apague.
void loop(){
  //Si el valor que leemos de la fotoresistencia es mayor que el límite es porque le está dando la luz del laser y por tanto queremos que se apague.
  if (analogRead(foto0) > limite)
  {
     // Escribimos en el pin digital de nuestro LED el valor LOW, que hace que se apague.
     digitalWrite(led0,LOW);
     // Hacemos sonar durante 300ms el tono de 523Hz.
     tone(zumb, 523, 300);
  }
if (analogRead(foto1) > limite)
  {
     digitalWrite(led1,LOW);
     tone(zumb, 523, 300);
  }
if (analogRead(foto2) > limite)
  {
     digitalWrite(led2,LOW);
     tone(zumb, 523, 300);
  }
if (analogRead(foto3) > limite)
  {
     digitalWrite(led3,LOW);
     tone(zumb, 523, 300);
  }
                
}  

Vídeo de solución del reto 2.1:

Hoy vamos hacer dos cosas, la segunda será programar la lógica base del juego:

  • ¿Cómo se van a encender y apagar os LEDs? Hasta ahora todos los LEDs se encendían al principio y se apagaban cuando les disparábamos, pero vamos a pasar a que se enciendan y apaguen de forma aleatoria.
  • ¿Cuándo vamos a perder? Cuando se nos escapen 5 enemigos.

Pero la primera va a consistir en dejar más bonito nuestro código (y también más facil de leer y de entender) gracias a la magia de las funciones.

¿Cómo vamos a hacer todo esto? Muy sencillo:

La magia de las funciones

Primero un ejemplo muy sencillo de funciones: Vamos a crear unas funciónes llamadas apagarLED(led_que_queremos_apagar) y encenderLED(led_que_queremos_encender) para no tener que utilizar digitalWrite(led,HIGH) y digitalWrite(led,LOW) siempre.

¿Cómo funcionan las funciones? Cada vez que necesitemos utilizar la función digitalWrite(led,HIGH) escribiremos apagarLED(led_que_queremos_apagar) en su lugar.

¿Cómo sabe el programa lo que tiene que tiene que hacer al leer apagarLED(led_que_queremos_apagar)? Porque en la parte final del código, debajo del loop(), tenemos que describir lo que hacen las funciones que vayamos creando.

// La función encenderLED enciende el LED que le digamos entre paréntesis.
void encenderLED(int led){
  digitalWrite(led,HIGH);
}
//La función apagarLED apaga el LED que le digamos entre paréntesis.
void apagarLED(int led){
  digitalWrite(led,LOW);
}

El cambio de digitalWrite(led,LOW) a apagarLED(led) es muy pequeño, solo nos ha servido para traducir a una línea de código, pero las funciones están pensadas para poder simplificar grandes códigos escribiéndolos en sitios que no sean el bucle principal, de esta forma el código es más facil de enteder.

Ahora vamos a cambiar toda la parte del loop(), que en la resolución del reto se ve bastante grande, por esto:

void loop(){
  disparando();              
} 

Ese disparando() es una función.

Y todo lo que antes estaba en el Ioop() ahora va a estar dentro de la función disparando() que vamos a escribir al final del código (debajo de loop).

void disparando(){
  if (analogRead(foto0) > limite){
       apagarLED(led0);
       tone(zumb, 523, 300);
    }
  if (analogRead(foto1) > limite){
       apagarLED(led1);
       tone(zumb, 523, 300);
    }
  if (analogRead(foto2) > limite){
       apagarLED(led2);
       tone(zumb, 523, 300);
    }
  if (analogRead(foto3) > limite){
      apagarLED(led3);
      tone(zumb, 523, 300);
    }
}

¿Cómo se van a encender y apagar los LEDs?

Hasta ahora todos los LEDs se encendían al principio y se apagaban cuando les disparábamos, pero vamos a pasar a que se enciendan y apaguen de forma aleatoria.

Para eso vamos a utilizar la función random(min,max), una función que devuelve un valor entre el valor mínimo y el máximo que le indiquemos. Por ejemplo: Si yo pongo int numero = random(0,100), en la variable numero se va a guardar un número entre el 0 y el 100.

Así que, lo primero que tendremos que hacer para poder encender los LEDs de forma aleatoria, es borrar el trozo de código que hacía que se encendieran todos los LEDs al principio.

void setup() {
.
.
.
  //Encendemos los LEDs.  
  digitalWrite(led0,HIGH);
  digitalWrite(led1,HIGH);
  digitalWrite(led2,HIGH);
  digitalWrite(led3,HIGH);
}

Después creamos un contador, podemos llamarle contador, que se va a encargar de que no se encienda o apague un led cada vez que el arduino lea el blucle loop(), porque tenemos que recordar que el arduino lee el código de loop() muchas veces en un segundo.

Cada vez que el arduino entre en el código disparando() vamos a aumentar el valor del contador al final.

void disparando(){
  if (analogRead(foto0) > limite){
       apagarLED(led0);
    }
  if (analogRead(foto1) > limite){
       apagarLED(led1);
    }
  if (analogRead(foto2) > limite){
       apagarLED(led2);
    }
  if (analogRead(foto3) > limite){
      apagarLED(led3);
    }

  //Poner contador++ es equivalente a poner contador=contador+1. Aumentando así el valor del contador en una unidad. 
  contador++;
}

NOTA: El contador hay que inicializarlo al principio del código.

int contador = 0;

¿Cómo quedaría el bucle loop()? Al entrar en él, lo primero que haría el programa es leer lo que pone en la función disparando(). Depués miraría el contador, y si el valor que tiene es mayor que 5000 ponemos el contador a 0, para que todo vuelva a empezar, y el programa lee la función encenderapagarLEDaleatorio().

¿Qué pasa si el contador es menor que 5000? No pasa nada, el bucle vuelve a empezar y el programa vuelve a leer lo que haya dentro de la función disparando().

void loop(){
  disparando();  
  if (contador > 5000)  {
    contador = 0;
    encenderapagarLEDaleatorio();
    }          
} 

De esta forma, solo 1 de cada 5000 veces que se lea el bucle loop() se va a entrar en la función que permite encender y apagar los LEDs. Si en vez de 5000 ponemos un número más grande, los LEDs se encenderán y apagarán más despacio.

Ahora vamos a programar la función encenderapagarLEDaleatorio() que va a encargarse de encender y apagar los LEDs de forma aleatoria. [El nombre es bastante descriptivo].

¿Cómo va a funciónar? Le vamos a decir que elija un número aleatorio entre 0 y 4 (para que de de resultado los números que tienen asignados nuestros leds y nuestras fotoresistencias): Si el led elegido está encendido, queremos que lo apague. Si el led elegido está apagado, queremos que lo encienda.

Pero, ¿cómo sabemos si el led está encendido o apagado? Con un tipo de dato llamado bool, que puede estar a true o a false, inicializamos una variable para cada LED:

bool encendido0 = false;
bool encendido1 = false;
bool encendido2 = false;
bool encendido3 = false;

Lo ponemos a false al inicializarlo porque al principio los LEDs van a estar apagados.

NOTA: Cada vez que encendamos un LED hay que escribir encendidoX=true y cada vez que apaguemos un LED hay que escribir encendidoX=false, para que la lógica de todo el juego tenga sentido.

¿Cómo quedaría la función disparando()?

void disparando(){
  if (analogRead(foto0) > limite){
       apagarLED(led0);
       encendido0 =false;
    }
  if (analogRead(foto1) > limite){
       apagarLED(led1);
       encendido1 =false;
    }
  if (analogRead(foto2) > limite){
       apagarLED(led2);
       encendido2 =false;
    }
  if (analogRead(foto3) > limite){
      apagarLED(led3);
      encendido3 =false;
    }

  //Poner contador++ es equivalente a poner contador=contador+1
  contador++;
}

¿Cómo quedaría el código de la función encenderapagarLEDaleatorio()?

Al empezar la función se tiene que generar un número aleatorio, que es el que va a decidir qué led se enciende o apaga. Una vez decidido el led, si está encendido se apaga y si esta apagado se enciende.

void encenderapagarLEDaleatorio(){
  //Elegimos el LED de forma aleatoria.
  int n = random(0,4);

  // Si toca el LED 0:
  if (n == 0){
    if (encendido0){
      apagarLED(led0);
      encendido0 = false;
    }else {
      encenderLED(led0);
      encendido0 = true;
    }
  }
  // Si toca el LED 1:
  if (n == 1){
    if (encendido1){
      apagarLED(led1);
      encendido1 =false;
    }else {
      encenderLED(led1);
      encendido1 = true;
    }
  }
  // Si toca el LED 2:
  if (n == 2){
    if (encendido0){
      apagarLED(led2);
      encendido2 =false;
    }else {
      encenderLED(led2);
      encendido2 = true;
    }
  }
  // Si toca el LED 3:
  if (n == 3){
    if (encendido3){
      apagarLED(led3);
      encendido3 =false;
    }else {
      encenderLED(led3);
      encendido3 = true;
    }
  }
}

¿Cuándo vamos a perder?

Cuando se nos escapen 5 enemigos.

¿Cómo vamos a saber cuántos enemigos se han escapado? Vamos a tener una variable parecida a contador llamada «escaparon» y cada vez que el programa apague un LED de forma aleatoria, y no porque nosotras le hayamos disparado, va a aumentar en uno (1) el valor de «escaparon«.

int escaparon = 0;

¿Cómo le decimos al programa que el juego termina cuando se escapen cinco (5) enemigos? Al principio del bucle loop() ponemos la condición de que solo se pueda jugar si el número de enemigos es menor a cinco (5).

Si el número de enemigos que se han escapado es mayor que 5 (cinco) los LEDs se apagan y termina el juego.

Código final:

const int led0 = 7;
const int led1 = 8;
const int led2 = 12;
const int led3 = 13;

const int foto0 = A0;
const int foto1 = A1;
const int foto2 = A2;
const int foto3 = A3;

const int limite = 100;
const int zumb = 9;

int contador = 0;
int escaparon = 0;

bool encendido0 = false;
bool encendido1 = false;
bool encendido2 = false;
bool encendido3 = false;
 
// Recordatorio: setup() solo lo va a leer el arduino cuando se encienda, nunca más.
void setup() {
  Serial.begin(9600);   
  pinMode(led0 , OUTPUT);
  pinMode(led1 , OUTPUT);
  pinMode(led2 , OUTPUT);
  pinMode(led3 , OUTPUT);    
  pinMode(foto0 , INPUT);   
  pinMode(foto1 , INPUT);
  pinMode(foto2 , INPUT);
  pinMode(foto3 , INPUT);
}

// Recordatorio: loop() lo lee el arduino en bucle hasta que se apague.
void loop(){
  // ¿Estamos disparando a nuestros enemigos? Pues tenemos una función que se encarga de apagar el LED que corresponda en caso de que acertemos en el enemigo.
 if (escaparon < 5){
    disparando();  
    if (contador > 5000)  {
      contador = 0;
      encenderapagarLEDaleatorio();
      }          
  } else{
    apagarLED(led0);
    apagarLED(led1);
    apagarLED(led2);
    apagarLED(led3);
  }
}



void disparando(){
  if (analogRead(foto0) > limite){
       apagarLED(led0);
       tone(zumb, 523, 300);
       encendido0 = false;
    }
  if (analogRead(foto1) > limite){
       apagarLED(led1);
       tone(zumb, 523, 300);
       encendido1 = false;
    }
  if (analogRead(foto2) > limite){
       apagarLED(led2);
       tone(zumb, 523, 300);
       encendido2 = false;
    }
  if (analogRead(foto3) > limite){
      apagarLED(led3);
      tone(zumb, 523, 300);
      encendido3 = false;
    }
  contador++;
}



void encenderLED(int led){
  digitalWrite(led,HIGH);
}
//La función apagarLED apaga el LED que le digamos entre paréntesis y hace sonar el zumbador.
void apagarLED(int led){
  digitalWrite(led,LOW);
}



void encenderapagarLEDaleatorio(){
  int n = random(0,4);
  if (n == 0){
    if (encendido0){
      apagarLED(led0);
      escaparon ++;
      encendido0 =false;
    }else {
      encenderLED(led0);
      encendido0 = true;
    }
  }
  if (n == 1){
    if (encendido1){
      apagarLED(led1);
      encendido1 =false;
      escaparon ++;
    }else {
      encenderLED(led1);
      encendido1 = true;
    }
  }
  if (n == 2){
    if (encendido0){
      apagarLED(led2);
      encendido2 =false;
      escaparon ++;
    }else {
      encenderLED(led2);
      encendido2 = true;
    }
  }
  if (n == 3){
    if (encendido3){
      apagarLED(led3);
      encendido3 =false;
      escaparon ++;
    }else {
      encenderLED(led3);
      encendido3 = true;
    }
  }
}

RETO DIARIO :

RETO 4.1: Simplificar el bucle loop().

Dejar el bucle loop() así:

void loop(){
 if (escaparon < 5){
    jugar();       
  } else{
    game_over();
  }
} 

RETO 4.2: Programar la victoria.

De la misma forma que se pone una variable escaparon para contar hasta 5 (cinco) y poder decir que hemos perdido, también se puede añadir una variable victoria que cuente hasta 20 (veinte).

Tenéis para entregar las soluciones de los retos hasta hoy antes de las 21:30 en el drive como todos los días.

¡Que la suerte esté siempre de vuestra parte!

Deja una respuesta

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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.