PiGiTi

Autores

ADRIÁN GÓMEZ-LOBO NUÑEZ
JOSE MANUEL PASTOR GONZÁLEZ
JORGE BERNABÉ MOLINERO
ALONSO MARTÍN ARROYO

Índice

  • Introducción
  • Planteamiento
  • Materiales / Precio
  • Circuito
  • Proceso
    • Código
    • Ensamblaje
    • Dificultades
  • Funcionamiento
  • Vídeo del funcionamiento

Introducción

El origen del proyecto surge de la votación de las diferentes ideas que tuvimos  para el proyecto se barajó desde la idea de una alarma, un robot, incluso una gorra inteligente pero finalmente nos decantamos por la idea de un Bob It, que es un juguete interactivo y electrónico diseñado para desafiar la coordinación, los reflejos y la capacidad de seguir instrucciones rápidas Al que bautizamos PiGiTi

Planteamiento

Pensar los diferentes minijuegos en los que se incluirían en el proyecto y los diferentes materiales extras que necesitáramos para llevarlos a cabo. Teníamos claro desde el principio que además del material ofrecido en la asignatura necesitábamos botones, algo que se pudiera girar como una tuerca, un sensor que pudiera detectar el movimiento y algún tipo de mp3 para el audio. Aunque también añadimos una matriz que refleje de forma más clara las instrucciones que debía seguir la persona que lo pruebe.

Después crear un código que cumpla las funciones de los diferentes minijuegos planteados y cuando tuviéramos los materiales ir probando uno por uno los diferentes juegos hasta poder implementarlos todos.

Y finalmente la construcción del proyecto en sí.

Materiales / Precio

Clase (Precio Estimado €)Añadido (Precio €)
Placa Arduino 5,99€Mini MP3 + Lector SD 9,99€
Protoboard 4,99€Módulo de pantalla LED 10,99€
Cables 0,50€Botones grandes 12X12MM 11,49€
Sensor inclinación 0,71€Caja de metal 14,99€
Potenciómetro 2€
Total: 14,19Total: 47,46€

Circuito

Para representar el circuito hemos usado un diagrama de tinkercad, en la primera ilustración debido a la falta de componentes de la herramienta, falta por representar el altavoz integrado y la pantalla led usada en el circuito completo, los cuales están representados en las siguientes dos ilustraciones del circuito. En el circuito final todos los componentes mencionados son añadidos en un único circuito.

Proceso

Código

// C++ code
//
#include <MD_MAX72xx.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>

//BOTONES

int buttonPinR = 2;
int buttonPinG = 3;
int buttonPinB = 4;
int buttonPinA = 5;

//lectura valor botones

 int _entradaR,_entradaG,_entradaB,_entradaA;
//DISPLAY
SoftwareSerial mySerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
//POTENCIOMETRO
int potPin=0;
//Tilt Sensor
int tiltPin=6;
// MATRIX DISPLAY
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4      // Número de módulos (4 en este caso)
#define CLK_PIN 13         // Pin CLK
#define DATA_PIN 9        // Pin DIN
#define CS_PIN 8          // Pin CS

// Crear una instancia del display
MD_MAX72XX matrix = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

// Variables de control de juego
int delayTime=750;
int nJugadores;
int nMiniGames=4;
enum colors{black,red,green,blue,yellow};
bool interrupt=false;
// Variables de juego
int players[4]={1,2,3,4};
int puntuation[4];
int miniGamesPlayed[4];
int basePuntuation=50;

////FUNCIONES//////////



////FUNCIONES AUX////////////////////////////
void ButtonsReading(){//Lectura de los botones
   _entradaR=digitalRead(buttonPinR);
   _entradaG=digitalRead(buttonPinG);
   _entradaB=digitalRead(buttonPinB);
   _entradaA=digitalRead(buttonPinA);
}
void ResetButtons(){//Resetear botones
  _entradaR=LOW;
   _entradaG=LOW;
   _entradaB=LOW;
   _entradaA=LOW;
}
int WhatButton(){
  //Devuelve que boton se ha pulsado y resetea el valor de entrada
   if(_entradaR==HIGH){
    _entradaR=LOW;
    return red;
    
  }
  else if(_entradaG==HIGH){
    _entradaG=LOW;
    return green;
   
  }
  
  else if(_entradaB==HIGH){
    _entradaB=LOW;
    return blue;
    
  }
  else if(_entradaA==HIGH){
    _entradaA=LOW;
    return yellow;
    
  }
  else{
   Serial.println("Error, demasiados botones pulsados"); 
    return -1;
  }
}
String ColorToString(int color){
  switch(color){
    case black:
    return "NEGRO";
    break;
    
    case red:
    return "ROJO";
    break;

    case green:
    return("VERDE");
    break;

    case blue:
    return("AZUL");
    break;

    case yellow:
    return("AMARI");
    break;
    
  }
  return "NOCOLOR";
}
void PrintColor(int color){
  //Imprime por pantalla el color
  switch(color){
    case black:
    Serial.print("Negro");
    break;
    
    case red:
    Serial.print("Rojo");
    break;
    case green:
    Serial.print("Verde");
    break;
    case blue:
    Serial.print("Azul");
    break;
    case yellow:
    Serial.print("Amarillo");
    break;
  }
}
void ButtonPause(int mode){
  String message;
  
  switch(mode){
    case 1:
    message="PULSA PARA CONTINUAR";
    break;
    case 2:
    break;
    case 3:
    message="OPERACION CANCELADA";
    break;
    case 4:
    message="ENHORABUENA JUGADOR ";
    message+=String(players[0]);
    message+=": ";
    message+=String(puntuation[0]);
    message+=" PUNTOS";


  }
  int offset=0;
  ResetButtons();
 int time=millis();
 int timePassing; 
 int timeToFinish=10000;
  while(_entradaR==LOW && _entradaG==LOW &&
        _entradaB==LOW && _entradaA==LOW){
    display(message,offset);
    offset--;
    ButtonsReading();
    timePassing=millis()-time;
    //Serial.println(timePassing);
    if(timePassing>timeToFinish){
      PlayAudio(23);
      time=millis();
      timeToFinish+=5000;
      
    }
      }
  	ResetButtons();
}
void PotenciometerPause(int deltaRange){
  //deltaRange dice de cuanto rango tiene que ser el giro para
  //que haya valido el giro al potenciometro
 int _inPotNew;//lecturapotenciometro
 _inPotNew=analogRead(potPin);
 int _inPotOld=_inPotNew;
  while(Abs(_inPotNew-_inPotOld)<deltaRange){
    _inPotOld=_inPotNew;
    _inPotNew=analogRead(potPin);
   // Serial.print(_inPotNew-_inPotOld);
  }
}
int Abs(int a){
return a= a<0 ? -a:a; 
}
String InvertirCadena(String cadena){
  String inverso;
  for(int i=0;i<cadena.length();i++){
    inverso+=cadena[cadena.length()-i-1];
  }
return inverso;
}
void OrderMaxToMin(){
 int n=4;
  //Algoritmo de ordenacion de mayor a menor
   for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (puntuation[j] < puntuation[j + 1]) {
                // Intercambiar elementos
                int temp = puntuation[j];
                puntuation[j] = puntuation[j + 1];
              	puntuation[j + 1] = temp;
               	int tempPlayer = players[j];
                players[j] = players[j + 1];
              	players[j + 1] = tempPlayer;
            }
        }
    }
  
}
void display(String message, int offset) {
//31 izquierda del todo
//0 derecha del todo
//esquina inferior izquierda + 4 derecha(5 total ocupa)
    // Borra el display
    matrix.clear();
    int totalMessageSpace = message.length() * 5; //el tamaño de cada caracter es de 5 celdas de la matriz
    //Serial.print(message.length());
    
    if (totalMessageSpace+ message.length() < 32) {
        //Centrar si se puede
        int borderSpace;
        if(offset==0) borderSpace=(32-totalMessageSpace)/2;
        else borderSpace=0;
        offset=offset%32;
        
        //Serial.print(borderSpace);
        //Empezar por borderSpace
            for (int i = message.length()-1; i >=0 ; i--) {
              int position=(31-(i)*5-borderSpace-offset);
                matrix.setChar(position, message[i]);
                if((position<5)&&offset!=0)//Si está llegando al borde de la derecha aparece por la izquierda duplicado
                  matrix.setChar(31+position,message[i]);
                if(position>31&&offset!=0){
                  matrix.setChar(-31+position,message[i]);
                }
        }
       
        //Preparar mensaje
        matrix.update();
    }
    else {//necesita un tratamiento distinto al ser la cadena más grande, sólo puede mostrar el inicio y fin
        offset=offset%totalMessageSpace;//el offset ha de ser tamaño cadena
        matrix.clear();
         
        for (int i = message.length()-1; i >=0 ; i--) {
          
              int position=(31-(i)*5-offset);//empieza izquierda(offset=0)
              //checkear si está a la vista
              if(position<-(totalMessageSpace-32))//Si está fuera de limites y tu posicion supera el limite
                position=31+position%(totalMessageSpace-32);
              if(position>(totalMessageSpace-32))
                position=-31+position%(totalMessageSpace-32);
                 // position=16-i*5-offset%32;
                
                matrix.setChar(position, message[i]);

               
                  
        }
        matrix.update();
    }
    
    


}
void displayStatic(String message){
  delay(delayTime);
  for(int i=0;i<10;i++){
    display(message,0);
  }
}

void displayNumbers(int timeMill) {//tiempo en segs
int mode=1000;//mins:segs o segs:mill
   int minutos = timeMill / mode;  // Divide el tiempo total para obtener los minutos.
    int segundos = timeMill % mode; // Obtiene los segundos restantes.
    
    // Si los segundos son 0 pero hay minutos, ajustamos para que se muestren como 59 segundos.
    // if (segundos == 0 && minutos > 0) {
    //     minutos--;     // Resta un minuto.
    //     segundos = 59; // Ajusta los segundos.
    // }
    
    String timeSegs = InvertirCadena(String(segundos));
    String timeMins = InvertirCadena(String(minutos));
    if(timeMins[0]=='1'||timeMins[1]=='1'||timeSegs[0]=='1'||timeSegs[1]=='1') matrix.clear(); //Absurdo de cojones, pero el paso del dos al uno está buggeado
    char uno = (timeMins[1] != '\0') ? timeMins[1] : '0';
    char dos = (timeMins[0] != '\0') ? timeMins[0] : '0';
    char tres = (timeSegs[1] != '\0') ? timeSegs[1] : '0';
    char cuatro = (timeSegs[0] != '\0') ? timeSegs[0] : '0';
        matrix.setChar(28,uno);
        matrix.setChar(22,dos);
        matrix.setChar(16, ':');
        matrix.setChar(13,tres);
        matrix.setChar(7, cuatro);
        
       
    matrix.update();



}

void PlayAudio(int track) {

 // Detener el audio actual si hay alguno reproduciéndose
  if (myDFPlayer.readState()== 513) {
    myDFPlayer.stop();
    Serial.println("Audio interrumpido.");
    delay(100); // Pequeño retardo para asegurar la detención
  }
 // Serial.println(myDFPlayer.readState());
  Serial.print("Reproduciendo pista: ");
  Serial.println(track);
  myDFPlayer.play(track);
  
}
void ResetAllVariables(){
  matrix.clear();
  ResetButtons();
  nJugadores=0;
  for (int i=0;i<4;i++){
  players[i]=i+1;
  puntuation[i]=0;
  }
}
//FUNCIONES ESTADO 1/////////////////////////
int SelectPlayers(int seleccion){
 
  //Mientras que no se pulse un botón se mantiene dentro del bucle
  //el bucle leyendo una entrada
int offset=0;

  while(_entradaR==LOW && _entradaG==LOW &&
        _entradaB==LOW && _entradaA==LOW){
    String selectMessage;
    if(seleccion==-1){
      selectMessage="SELECCIONE EL NUMERO DE JUGADORES";
    }
    else{
      selectMessage="CONFIRMAR ";
      selectMessage+=String(seleccion);
      selectMessage+=" JUGADORES";
      
    }
    display(selectMessage,offset);
    offset--;
    ButtonsReading();

  }
 // Serial.println("boton pulsado");
  return WhatButton();
 //Serial.println(nJugadores);
  delay(delayTime); 
  return -1;
}
//FUNCIONES ESTADO 2/////////////////////////
bool PlayAMiniGame(int player,int minigamesCount){
  //Esta funcion es la terminal donde van a apareciendo los minijuegos
  //Recibe como argumentos el jugador actual y cuantos juegos lleva
  //minigamesCount sirve para ajustar la dificultad de cada juego(reducir tiempos de respuesta, aumentar el numero de iteraciones etc)
  
  int _minigame=(minigamesCount-1)%nMiniGames+1;
  //_minigame=2;
  bool _miss;
  //los minijuegos devuelven un booleano
  //si se ha fallado o no
  //Estructura para crear un minijuego
  //_miss=NombreMinijuego(player,minigamesCount);
  
  displayStatic("LISTO");
  PlayAudio(18);
 
  Serial.println("Simon dice comienza\n");
  switch(_minigame){
    case 1:
    	_miss=SimonSaysButtons(player,minigamesCount);
    break;
    case 2:
    	_miss=SimonSaysPotenciometer(player,minigamesCount);
    break;
    case 3:
    	_miss=SimonSaysTiltSensor(player,minigamesCount);
    break;
    case 4:
    	_miss=SimonSaysEnd(player,minigamesCount);
   	break;
    default:
    _miss=true;
    break;
  }
  
  return _miss;
}
//Minijuegos
bool SimonSaysButtons(int player, int minigamesCount){
 int nIteraciones=rand()%3+minigamesCount;
 int _ranCol;
 bool _miss;
 
  //variable bonus puntuacion
  int _timeBonus=0;
  //variables que miden el tiempo
  //Son locales porque cada juego creo
  //que necesitara sus propios ajustes
	  int _timeToFinish=4000;//Se empieza con un margen de 2.5 segs
    int steps=150;//Cada vez hay menos tiempo
  	int unsigned long _time;//Mide el tiempo inicial
  	int _minReactionTime;//Tiempo minimo reaccion
  	int _timePassing;
  /////////////////////////////
  for(int i=0;i<nIteraciones;i++){//Es finito con n iteraciones
  _ranCol=random(1, 5);
 // rand()%4+1;
    Serial.print("Pulsa el boton ");
    PrintColor(_ranCol);
    Serial.println("");
    PlayAudio(19+_ranCol-1);
    String pulsa=ColorToString(_ranCol);
    
      displayStatic(pulsa);
    
    _time=millis();
    while(_entradaR==LOW && _entradaG==LOW &&
        _entradaB==LOW && _entradaA==LOW){
      
    	ButtonsReading();
      _timePassing=millis()-_time;//Tiempo transcurrido
      if(_timePassing>=_timeToFinish){
        //Serial.println(millis());
        //Serial.println(_time);
        //Serial.println(millis()-_time);
        Serial.println("Womp, womp, demasiado lento");
        displayStatic("LENTO");
        PlayAudio(23);
        return true;
      }
		//Serial.println(_timeToFinish-_timePassing);
      //Cuenta atrás usar display
  	}
    
      if(WhatButton()==_ranCol){
      Serial.println("Bien! Continuamos...");
      displayStatic("BIEN");
      PlayAudio(25);
        _timeBonus=+(_timeToFinish-_timePassing)/100;
        //Se resta el tiempo con clamp en _minReactionTime
       _timeToFinish=_timeToFinish-steps>_minReactionTime?_timeToFinish-steps:_minReactionTime;
        
     }else{
       Serial.println("Estas ciego?");
       displayStatic("ERROR");
        PlayAudio(26);

       return true;//falla, se termina minijuego
  	}
  }
 
      displayStatic("PUNTOS");

  Serial.println("Enhorabuena, sumando puntuaciones...");
  
  PlayAudio(27);
  delay(delayTime);
  puntuation[player-1]+=(basePuntuation*minigamesCount)/4+
    basePuntuation*nIteraciones+_timeBonus;
 return false;
  
}
bool SimonSaysPotenciometer(int player,int miniGamesCount){
  
  
  int maxRange=400;
  int deltaRange=miniGamesCount*100<maxRange ? miniGamesCount*100 : maxRange;
  int _inPotNew;//lecturapotenciometro
 _inPotNew=analogRead(potPin);
 int _inPotOld=_inPotNew;
  //variable bonus puntuacion
  int _timeBonus=0;
  //variables que miden el tiempo
  //Son locales porque cada juego creo
  //que necesitara sus propios ajustes
  int _minReactionTime=500;//Tiempo minimo reaccion
  int _maxTime=7000;
	int _timeToFinish=_maxTime-miniGamesCount*100>_minReactionTime?_maxTime-miniGamesCount*100:_minReactionTime;//Se empieza con un margen de 2.5 segs
    int steps=100;//Cada vez hay menos tiempo
  	int unsigned long _time=millis();//Mide el tiempo inicial
  	int _timePassing;
  Serial.print("GIRA LA RUEDITA\n");
  displayStatic("GIRA");
  PlayAudio(28);
  while(Abs(_inPotNew-_inPotOld)<deltaRange){
    _inPotOld=_inPotNew;
    _inPotNew=analogRead(potPin);
    _timePassing=millis()-_time;
     _timeBonus=+(_timeToFinish-_timePassing)/100;
     displayNumbers(_timeToFinish-_timePassing);
    if(_timePassing>=_timeToFinish){
      
      Serial.print("Te pasa algo en las manos?\n");
      displayStatic("LENTO");
      PlayAudio(29);
      delay(delayTime);
     return true;
    }
  }
Serial.print("Enhorabuena, sumando puntuacion...\n");
  displayStatic("BIEN");
  PlayAudio(32);
  PlayAudio(27);
  puntuation[player-1]+=(miniGamesCount+_timeBonus)*5;
  return false;
}
bool SimonSaysTiltSensor(int player, int miniGamesCount){
  //Este minijuego lo que hace es pedirte que inclines o dejes 
  //en reposo el cacharro según como lo tengas sujeto(lo contrario)
  
  int tiltState = digitalRead(tiltPin); // Lee el estado del sensor
  int condition;
  
  if (tiltState == LOW) { // LOW significa que el circuito está cerrado (sensor inclinado)
    condition=HIGH;
    PlayAudio(30);//Inclina
  } else {
    condition=LOW;
    PlayAudio(31);//Estabiliza
  }
  //variable bonus puntuacion
  int _timeBonus=0;
  //variables que miden el tiempo
  //Son locales porque cada juego creo
  //que necesitara sus propios ajustes
  int _minReactionTime=500;//Tiempo minimo reaccion
  int steps=100;//Cada vez hay menos tiempo
  int _maxTime=6000;
	int _timeToFinish=_maxTime-miniGamesCount*steps>_minReactionTime?_maxTime-miniGamesCount*steps:_minReactionTime;//Se empieza con un margen de 2.5 segs
  	int unsigned long _time=millis();//Mide el tiempo inicial
  	int _timePassing;
  Serial.print("INCLINA EL CACHARRO\n");
  displayStatic("TILT");
  while(tiltState!=condition){
    tiltState = digitalRead(tiltPin);
     displayNumbers((_timeToFinish-_timePassing));
    _timePassing=millis()-_time;
    _timeBonus=+(_timeToFinish-_timePassing)/100;
   

    if(_timePassing>=_timeToFinish){
      Serial.print("Eres lento?\n");
      displayStatic("LENTO");
      PlayAudio(24);
      delay(delayTime);
     return true;
    }
  }
  Serial.print("Bravo, sumando puntuaciones\n");
  displayStatic("BIEN");
  PlayAudio(32);
  puntuation[player-1]+=(miniGamesCount+_timeBonus)*2;
  return false;
}

bool SimonSaysEnd(int player, int miniGamesCount){
 int nIteraciones=rand()%3+miniGamesCount;
 bool _miss;
 Serial.println("Listo para un reto?\n");
 displayStatic("BANZAI");
 PlayAudio(1);
 delay(delayTime+500);
 /////////////////////////////
  
  for(int i=0;i<nIteraciones;i++){//Es finito con n iteraciones
    delay(100);
    int randomMinigame=random(1, 4);
    //rand()%3+1;
    
    //Serial.print(randomMinigame);
    switch(randomMinigame){
    case 1: //Botones
   		_miss=SimonSaysButtons(player,0);
    break;
   
    case 2: //Potenciometro
      _miss=SimonSaysPotenciometer(player,miniGamesCount);
    break;
    case 3: //Tilt Sensor
      _miss=SimonSaysTiltSensor(player,miniGamesCount);
     break;
    
    }
    if(_miss) return _miss;
  }
  
  
  
  Serial.println("Enhorabuena, sumando puntuaciones extra...");
  PlayAudio(2);
  puntuation[player-1]+=(basePuntuation*miniGamesCount)/2+
  basePuntuation*nIteraciones;
 return false;
  
}
void setup()
{
  //srand(time(0));
 
  //BOTONES
  pinMode(buttonPinR, INPUT_PULLUP);
  pinMode(buttonPinG, INPUT_PULLUP);
  pinMode(buttonPinB, INPUT_PULLUP);
  pinMode(buttonPinA, INPUT_PULLUP);
  //POTENCIOMETRO
  pinMode(potPin,INPUT);
  //Tilt Sensor
  pinMode(tiltPin,INPUT_PULLUP);
  //DISPLAY
  matrix.begin();           // Inicializar  display
  matrix.control(MD_MAX72XX::INTENSITY, 5);  // Ajustar brillo (0-15)
  
  Serial.begin(9600);
  mySerial.begin(9600);

  if (!myDFPlayer.begin(mySerial)) {
    Serial.println("Error al inicializar DFPlayer Mini.");
    while (true);
  }
  Serial.println("DFPlayer Mini inicializado correctamente.");

}



void loop()
{
//  unsigned long seed = analogRead(A0); // Lee ruido del pin A0
//   randomSeed(seed); // Inicializa la semilla
//   Serial.print("Semilla generada: ");
//   Serial.println(seed);

//   Serial.println("Números aleatorios:");
//   for (int i = 0; i < 5; i++) {
//     Serial.println(random(0, 100)); // Genera números aleatorios entre 0 y 99
//   }

  //IMPORTANTE 
  //NOTAS
  //TODOS y digo TODOS los print serán sustituidos por los
  //mensajes de voz
  
 //Estado 0 Presentacion
  //En este estado se hace una pequeña presentación del juego
  //que alguien lo defina
  
 //Estado 1 Seleccion de jugadores
  //En este estado se selecciona el numero de jugadores
  
  //Ese valor se almacena en nJugadores
  
  //Hay que pulsar dos veces el mismo boton
  bool finishSelection=false;
  while(!finishSelection){//Mientras no se haya confirmado no se para

    Serial.println("Seleccione el numero de jugadores");
    PlayAudio(3);
   
  int _seleccion=SelectPlayers(-1);//Se selecciona por primera vez
  if(_seleccion!=-1){
        delay(500);
        Serial.print("Se van a seleccionar ");
        Serial.print(_seleccion);
        Serial.println(" jugadores.");
    Serial.println("Confirme el numero de jugadores");
    PlayAudio(4);
    if(SelectPlayers(_seleccion)==_seleccion){//Se confirma el numero
     	nJugadores=_seleccion;
     	Serial.print("Van a jugar ");
    	Serial.print(nJugadores);
    	Serial.println(" jugadores.");
      	finishSelection=true;
    }else{
     
       Serial.println("Cancelando");
       PlayAudio(5);
       delay(delayTime);
       ButtonPause(3);
       
       
    }
                   
  }
  }
  
 //Estado 2 Minijuegos
  Serial.println("Que comiencen los juegos");
  
  displayStatic("PLAY");
  PlayAudio(6);
  
  bool _miss=false;
  int _minigamesCount=0;
  
  Serial.println("Comienza el jugador 1");
  PlayAudio(8);
  Serial.println("Pulse cualquier boton para empezar");
  delay(delayTime);
  ButtonPause(1);
  for(int i=1;i<nJugadores+1;i++){//Por nJugadores hacer n partidas
    while(!_miss){
      _minigamesCount++;
      _miss=PlayAMiniGame(i,_minigamesCount);
      delay(delayTime);
    }
    _miss=false;//resetear el valor
    if(i+1!=nJugadores+1){
   
    Serial.print("Pasando al jugador ");
    displayStatic("NEXT");
    PlayAudio(12);
    
    Serial.println(i+1);
    _minigamesCount=0;
   	delay(delayTime);
    Serial.println("Pulse cualquier boton para empezar");
  	ButtonPause(1);
    PlayAudio(8+i);//Jugador 1+i
    }
    
  }
  //Estado 3 Puntuaciones
  Serial.println("Hora de ver las puntaciones");
  PlayAudio(13);
  delay(delayTime);
  for(int i=0;i<nJugadores;i++){
    Serial.print("El jugador ");
    Serial.println(players[i]);
    Serial.print("Puntuacion: ");
    Serial.println(puntuation[i]);
  }
  OrderMaxToMin();
  delay(delayTime);
  Serial.print("Enhorabuena al jugador ");
  Serial.println(players[0]);
  PlayAudio(14+players[0]-1);//Jugador 1+ players[0]-1 players va de 1 a 4
  Serial.print("Puntuacion de ");
  Serial.println(puntuation[0]);
 
  //FIN DE JUEGO resetear variables y dar un tiempo de espera
 delay(delayTime);
 ButtonPause(4);
  
  ResetAllVariables();
  delay(delayTime);
  Serial.println("Pulse cualquier boton para continuar");
  PlayAudio(7);
  ButtonPause(1);
  
}

Primero tuvimos que pensar los diferentes minijuegos que íbamos a incluir y programarlos, los cuales debían ser completados antes de que se acabara el tiempo, estos consisten en:

  1. Pulsa!: en que el juego dice el color del botón que el jugador debe pulsar y este debe accionar ese botón o de lo contrario pierde.
  2. Gira!: en el que el jugador debe girar una tuerca.
  3. Inclina / Estabiliza!: en el que el jugador debe girar el juguete.

Además se añadió la función de seleccionar la cantidad de jugadores pudiendo jugar  hasta un máximo de 4 jugadores y almacenando las puntuaciones de cada uno en función de la cantidad de minijuegos completados y revelando el ganador al final.

Todo esto queda encapsulado siguiendo una sencilla máquina de estados. Siendo estos estados: seleccionar jugadores, jugar partida y obtener resultados.

Este pequeño fragmento muestra cómo funciona el primer estado. Se selecciona el número de jugadores, si es el deseado se confirma. Si no se cancela y se vuelve a iterar. Esta selección viene dada por el botón pulsado.

Este otro fragmento mostraría lo que hace el segundo estado. Mientras que el jugador que está jugando no falle el minijuego este sigue jugando. En el caso de que falle se pasa al siguiente jugador. Entre turno y turno hay habilitada una pausa para que el jugador que entre tenga tiempo de prepararse.

En este último estado se muestra la puntuación final del jugador ganador. Con un sencillo algoritmo de ordenación se reordena el array de puntuaciones y con ello se muestra la del jugador con más puntuación. Después, se resetean todas las variables y el juego vuelve a comenzar.

Los minijuegos que aparecen son secuenciales. Esto significa que siguen un orden establecido. Primero empieza el juego de los botones, le sigue el del potenciómetro, luego el del tilt sensor y por último puede aparecer una sucesión de estos tres. La función devuelve el booleano “_miss” que indica si se ha fallado o no.

Este es un ejemplo sencillo de minijuego. Al entrar se preparan todas las variables. Se inician los tiempos y se ajusta la dificultad en base a la cuenta de minijuegos jugados. Después dentro del bucle while se espera hasta que el jugador consiga superarlo. En caso de que se acabe el tiempo la función devuelve true(ha fallado). Si lo supera calcula la puntuación según el tiempo en el que lo haya superado(cuanto más rápido mejor) y los minijuegos superados y devuelve false(no ha fallado).

Ensamblaje

Para empezar el ensamblaje del proyecto tuvimos que crear una caja en la que montaríamos todo el circuito y los componentes del proyecto. Para ello usamos un trozo de metal en el cual cortamos agujeros para los botones y un agujero un poco más grande para ocultar todo el hardware dentro y acoplar una pantalla. A su vez, también la pintamos de negro por estética.

Con la caja lista, tuvimos que comprar ciertos componentes antes de ponernos con el ensamblaje final: 

Unos botones nuevos más grandes y con colores distintos que se pudieran acoplar a la caja de manera sencilla y de un tamaño grande para que fuesen fáciles de usar.

Una pantalla led para usar de display en los juegos entre otras cosas.

Un lector de sd, una tarjeta sd y altavoz mini en el que se pueden reproducir varios sonidos distintos guardados previamente en la tarjeta sd.

Una vez teníamos todo comprado, antes de montarlo en la caja, tuvimos que conectar todo el circuito de manera robusta para evitar cortocircuitos o algún tipo de problema. Para ello tuvimos que soldar ciertos componentes que eran propensos a dar problemas, entre ellos soldamos el potenciómetro y el altavoz con estaño, de esta manera la conexión es estable y evitamos falsos valores.

En el siguiente paso integramos todos los componentes como los botones, pantalla, etc. en la caja, organizando todo el cableado y asegurándonos que las conexiones eran estables y no había ningún corto circuito.

Por último hicimos un testeo de todo el ensamblaje tanto se software como de hardware verificando que todo estuviese bien montado

Dificultades

A lo largo de todo el proceso se han tenido la complicaciones que cabrían esperar en un proyecto.

En cuanto al código este ha sido una fuente inagotable de problemas e inconvenientes. Esto se debe a la reducida capacidad de cómputo que ofrece la placa de arduino. No poder realizar lanzamiento de threads y ejecutar todo de forma secuencial ha supuesto tener que tomar desvíos y formas poco ortodoxas para conseguir el mismo funcionamiento. Además, al tener un código de un tamaño bastante considerable el rendimiento comienza a verse perjudicado. También se han conseguido resolver bugs y problemas con la lógica del juego después de realizar varias pruebas.

Han habido también problemas con el montaje y algunos componentes ya sea con algunos de los ofrecidos en la asignatura ya que debido al continuo uso de estos materiales en años anteriores no estaban en su mejor condición o por nuestros propios añadidos a la hora de intentar integrarlos al proyecto. Sobre todo los altavoces. Aunque finalmente conseguimos superar estos obstáculos.

Funcionamiento del juego

Primero se debe elegir el número de jugadores pulsando el botón correspondiente. Empezando por el número 1 siendo el color rojo y el resto yendo en escala hasta el 4. Después volver a pulsar el mismo para confirmar la acción ya que si se pulsa un botón distinto se cancela la selección. En cuanto se confirma el primer jugador este toma el PiGiTi y empieza a jugar a los diferentes minijuegos, además a medida que los vaya superando el tiempo para completar los minijuegos se irá reduciendo gradualmente aumentando su dificultad hasta que el jugador falle o no lo complete a tiempo, cuando ocurra se pasa al siguiente jugador, confirma que está listo y se repite el proceso hasta que hayan jugado todos, finalmente el PiGiTi revelará al ganador y su puntuación.

Vídeo del funcionamiento

También te podría gustar...