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,19 | Total: 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:
- 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.
- Gira!: en el que el jugador debe girar una tuerca.
- 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.