DESACTIVA LA BOMBA: Juego con Arduino y pantalla Nextion

Introducción

El juego «Desactiva la Bomba» desafía al jugador a una carrera contrarreloj. Al arrancar, en la esquina superior derecha de la interfaz aparece un temporizador regresivo que marca el tiempo disponible de 15 minutos para «desactivar» la bomba. El jugador debe ir completando, uno a uno, una serie de minijuegos (SimonGame, DodgeGame, StopTime y Don’t Push); al superar cada uno, se revela un dígito del PIN secreto. Una vez superados todos los retos, la pantalla muestra el PIN completo y ofrece al usuario un cuadro de texto en el que debe introducirlo. Si el PIN es correcto antes de que el temporizador llegue a 0, el sistema simula la desactivación de la bomba; en caso contrario, «explota», indicando el fracaso y permitiendo reiniciar la partida.

Importante: Cada fallo en cualquiera de los minijuegos resta 1 minuto del tiempo disponible para desactivar la bomba, aumentando la presión sobre el jugador.

Autores: Tarek Elshami Ahmed, Jorge Carbonero Asin, Álvaro Serrano Rodrigo

Objetivo del proyecto «Desactiva la bomba»

Desarrollar un juego interactivo basado en la desactivación de bombas que desafíe y sorprenda al usuario mediante minijuegos engañosos que ponen a prueba diferentes habilidades (memoria, reflejos, precisión temporal y especialmente autocontrol), creando una experiencia de tensión creciente donde cada reto superado revela una parte del código necesario para la desactivación final.

Los Minijuegos

SimonGame

Basado en el clásico «Simón dice», este minijuego pone a prueba la memoria del jugador:

  • La pantalla muestra cuatro paneles de colores (rojo, verde, azul y amarillo)
  • El sistema genera una secuencia aleatoria de colores, añadiendo un paso nuevo en cada ronda
  • El jugador debe repetir la secuencia tocando los colores en el mismo orden
  • Cada error en la secuencia resta 1 minuto del tiempo total disponible
  • Superar un número de dos rondas revela el primer dígito del PIN

DodgeGame

Un desafío de reflejos y coordinación:

  • El jugador controla un OVNI en la pantalla mediante un joystick analógico
  • Asteroides se mueven en trayectorias aleatorias por la pantalla
  • El objetivo es evitar cualquier colisión durante 30 segundos
  • Cada colisión con un bloque resta 1 minuto del tiempo total
  • Completar el tiempo sin colisiones revela el segundo dígito del PIN

StopTime

Un reto de percepción temporal:

  • Un contador regresivo parte de 5.00 segundos y desciende con precisión centésimal
  • A los 4.00 segundos, el contador desaparece de la vista
  • El jugador debe pulsar «Parar» cuando estime que el tiempo está entre 0.30 y 0.00 segundos
  • Fallar en la estimación resta 1 minuto del tiempo total
  • Acertar en este intervalo revela el tercer dígito del PIN

Don’t Push

Un desafío de control de impulsos:

  • Un botón parpadeante aparece en el centro de la pantalla
  • El objetivo es resistir la tentación de pulsarlo durante 30 segundos
  • Si el jugador presiona el botón, la bomba reduce su tiempo disponible en 1 minuto
  • Completar el tiempo sin interactuar revela el cuarto dígito del PIN

Introducción del Pin

El jugador puede intentar meter el pin en cualquier momento, pero si lo hace mal entonces perderá un minuto del tiempo total.

Si introduce el pin correctamente entonces la bomba se desactivará y podrás volver a jugar pasados 30 segundos.

Diagrama de flujo

Hardware Utilizado

Para el desarrollo del proyecto utilizamos los siguientes componentes:

ComponenteCantidadFunción
Arduino MEGA 25601Cerebro del sistema que ejecuta la lógica principal
Pantalla táctil Nextion NX8048P050-011C-Y1Interfaz gráfica e interacción con el usuario
Joystick analógico XY con botón1Control del personaje en DodgeGame
Botones con LED integrados4Interacciones físicas y feedback visual
Resistencias y cablesVariosConexiones y circuitos
Tabla de cartón pluma1Base para el montaje del proyecto

Arquitectura del Proyecto

Nuestro proyecto está organizado en dos grandes bloques técnicos:

1. Hardware

El sistema se apoya en la plataforma Arduino MEGA 2560 como unidad central de procesamiento, conectada a una pantalla táctil Nextion NX8048P050-011C-Y mediante comunicación serial. Además, incorpora un joystick analógico para control direccional y botones con LED integrados para interacciones físicas adicionales.

2. Software

El proyecto está implementado mediante un enfoque de programación modular y orientada a objetos, con dos componentes de software claramente diferenciados:

  • Código Arduino (C++): Gestiona la lógica del juego, procesamiento de entradas, comunicación serial y control de componentes físicos.
  • Código Nextion: Maneja la interfaz gráfica, interacciones táctiles y visualización de elementos en pantalla.

Montaje del Proyecto

El montaje físico del proyecto siguió un proceso metódico para garantizar la funcionalidad y robustez del sistema:

1. Diseño Inicial

Comenzamos con un diseño en papel de la distribución de componentes, considerando aspectos de ergonomía y funcionalidad.

2. Pruebas Individuales

Antes del montaje final, cada componente fue probado individualmente para verificar su funcionamiento:

  • Comprobación de la comunicación entre Arduino y Nextion
  • Test de respuesta del joystick y botones
  • Verificación de los LED integrados

3. Montaje Físico

El proceso de montaje incluyó:

  • Colocación de componentes sobre la base de cartón pluma
  • Soldadura de conexiones para garantizar estabilidad
  • Organización de cableado para facilitar mantenimiento

4. Integración y Pruebas Finales

Una vez completado el montaje, realizamos pruebas integrales del sistema:

  • Verificación de todos los minijuegos
  • Comprobación del temporizador global
  • Test de la lógica de desactivación

Desafíos y Soluciones

Durante el desarrollo enfrentamos varios desafíos técnicos significativos:

1. Conexiones Inestables de Botones LED

Problema: Inicialmente intentamos conectar los componentes sin soldadura, lo que resultaba en conexiones inestables y comportamiento errático del sistema.

Solución: Adquirimos un kit de soldador y realizamos conexiones soldadas permanentes. 

 

2. Limitaciones del Potenciómetro

Problema: Habíamos planeado un minijuego basado en colores RGB que requería valores de 0-255, pero nuestro potenciómetro solo proporcionaba valores en el rango 0-185.

Solución: Descartamos ese minijuego y desarrollamos una alternativa (Don’t Push) que se ajustaba mejor a las capacidades del hardware disponible.

3. Documentación Incompleta de Nextion

Problema: La documentación oficial del lenguaje de programación Nextion resultó insuficiente para algunas funcionalidades específicas que queríamos implementar.

Solución: Realizamos investigación en foros y múltiples pruebas de ensayo-error hasta conseguir el comportamiento deseado.

4. Control de Versiones del Archivo .HMI

Problema: GitHub no detectaba adecuadamente los cambios en el archivo game.HMI, lo que complicaba la colaboración entre miembros del equipo.

Solución: Implementamos un sistema de turnos estricto para trabajar en el archivo, junto con comunicación constante para evitar sobrescribir el trabajo de otros.

Reparto de Tareas

El proyecto se desarrolló con una clara distribución de responsabilidades:

  • Álvaro Serrano Rodrigo: Desarrollo y programación de DodgeGame y SimonGame.
  • Tarek Elshami Ahmed: Desarrollo e implementación de StopTime y Don’t Push.
  • Jorge Carbonero Asin: Implementación del temporizador global, gestión del PIN y diseño de interfaces gráficas.

El montaje físico del dispositivo fue un esfuerzo conjunto de todo el equipo, lo que permitió integrar de manera efectiva todos los componentes en un sistema funcional y atractivo.

Costes del Proyecto

ComponenteCantidadPrecio unitario (€)Precio total (€)
Pantalla táctil Nextion NX8048P050-011C-Y180,0080,00
Joystick para Arduino (XY con botón)13,003,00
Placa Arduino Mega 2560 (compatible)115,0015,00
Cables1 set (40 uds)4,004,00
Resistencias surtidas1 set2,002,00
Botones con LED integrados52,0010,00
Tabla de cartón pluma (aprox. 50×70 cm)15,005,00
Kit Soldador110,0010,00
Total129,00 €

Código del Proyecto

Código Arduino

El código Arduino está estructurado en varios archivos para mantener la modularidad y facilitar el mantenimiento:

Archivo principal: nextion.ino

#include "NextionSoftSerial.h"
#include "NextionObject.h"
#include "SimonGame.h"
#include "DodgeGame.h"

// Pines físicos
const int redPin = 9;
const int greenPin = 10;
const int bluePin = 11;
const int yellowPin = 12;
const int buttonRed = 4;
const int buttonGreen = 5;
const int buttonBlue = 6;
const int buttonYellow = 7;
const int joyX = A3;
const int joyY = A4;
const int buttonJoy = 24;

// SimonGame
const int simonEntradas[4] = {buttonRed, buttonGreen, buttonBlue, buttonYellow};
const int simonSalidas [4] = {redPin,   greenPin,   bluePin,   yellowPin};
SimonGame simon(simonEntradas, simonSalidas);
// DodgeGame
DodgeGame dodge(joyX, joyY);

// Estados de pantalla
String currentPage = "pageHome";

void setup() {
  char cmd[100];

  Serial.begin(115200);

  //-- INI NEXTION DISPLAY -------------------------------------------------------------------
  Serial2.begin(19200);             // Inicializa puerto serie Nextion DISPLAY
  initNEXTION();                    // Inicializa Nextion DISPLAY
  delay(250);
  sendNEXTIONcmd("rest");
  delay(1500);
     
  sendNEXTIONcmd("page 0");
  strcpy(cmd,"\r\nSYSTEM> Initializing ... Nextion_SoftSerial");
  Serial.println(cmd); 
  //sendNEXTIONcmd("xstr 0,18,390,18,1,RED,WHITE,0,1,1,\"SYSTEM> Initializing ... Nextion_SoftSerial\"");
  delay(1500);
  
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  pinMode(yellowPin, OUTPUT);

  pinMode(buttonRed, INPUT_PULLUP);
  pinMode(buttonGreen, INPUT_PULLUP);
  pinMode(buttonBlue, INPUT_PULLUP);
  pinMode(buttonYellow, INPUT_PULLUP);

  pinMode(joyX, INPUT);
  pinMode(joyY, INPUT);

}

void loop() {
  String ev = listenNEXTION();
  char cmd[100];

  if (ev != "") {
    Serial.print("Evento recibido: ");
    Serial.println(ev.c_str());
    
    if (ev==bStart) 
    {
      strcpy(cmd,"\r\nSYSTEM> bStart push");
      Serial.println(cmd);

      //sendNEXTIONcmd("xstr 0,18,390,18,1,RED,WHITE,0,1,1,\"SYSTEM> ... bStart push\"");
    }

    if (ev==bGame1){
      handlePageChange(2);
    }

    if (ev==bGame2){
      handlePageChange(3);
    }

    if (currentPage == "pageSimon") {
      if (ev == bGame1Start || ev == bGame1Retry || ev == bGame1Start2) {  // startSimonGame
        ev = "";
        Serial.print("Simon started");
        simon.playFullGame();
      } 
    }
    Serial.println("currentPage 1: "+ currentPage);

    if (currentPage == "pageDodge") {
      if (ev == bGame2Start) { 
        Serial.println("Juego Dodge iniciado");
        dodge.startGame();
      } else if (ev == bGame2End) {
        Serial.println("Juego Dodge finalizado");
        dodge.stopGame();
      }
    }

  }

  if (currentPage == "pageSimon") {
    simon.update();
  }
  
  if (currentPage == "pageDodge") {
    dodge.update();
  }
  /*
  if (digitalRead(buttonJoy) == HIGH) {
    sendNEXTIONcmd("game1=1");
    sendNEXTIONcmd("game2=1");
    sendNEXTIONcmd("game3=1");
    sendNEXTIONcmd("game4=1");
  }
  */
  delay(100);
}

// Manejo de cambio de página
void handlePageChange(int pageId) {
  String pageCommand;
  String prevPage = currentPage;
  
  switch(pageId) {
    case 0: 
      currentPage = "pageHome";
      pageCommand = "page 0";
      break;
    case 2:
      currentPage = "pageSimon";
      pageCommand = "page 2";
      break;
    case 3:
      currentPage = "pageDodge";
      pageCommand = "page 3";
      break;
    default: 
      currentPage = "unknown";
      pageCommand = "page 0";  // Por defecto volvemos a página 0
      break;
    
    if (prevPage == "pageSimon"){
      simon.stopGame();
    }
  }
  
  // Confirmar el cambio de página a Nextion
  sendNEXTIONcmd(pageCommand.c_str());
  
  Serial.print("Cambiando a página: ");
  Serial.println(currentPage);
  
  // Esperar un poco para que se complete el cambio
  delay(100);
}

NextionObject.h

Define los código hexadecimales de todos los eventos que puede enviar la pantalla Nextion

#ifndef _NEXTION_OBJECT_H_

  #define _NEXTION_OBJECT_H_

  // NEXTION OBJECT
  #define bStart         ("[65011ffffffffffff]")
  #define bGame1         ("[65141ffffffffffff]")
  #define bGame1Start    ("[ffffffa2ffa2ffe1]") 
  #define bGame1Start2   ("[ffe2ffa2ffa2ffe1]")
  #define bGame1End      ("[1affffffffffff]")
  #define bGame1Retry    ("[ffe1ffa2ffa2ffe1]")
  #define bGame2         ("[65151ffffffffffff]") 
  #define bGame2Start    ("[653151ffffffffffff]") 
  #define bGame2End      ("[ffe2ffe2ffe2ffe2]") 
  #define bReturn        ("[4ffffffffffff]")


#endif

Implementación de SimonGame.h

// SimonGame.h
#ifndef SIMON_GAME_H
#define SIMON_GAME_H

#include <Arduino.h>
#include "NextionSoftSerial.h"
#include "NextionObject.h"

class SimonGame {
public:
    static const int NIVEL_MAX = 6;

    static const char* const colorNames[4];
    // Ahora sólo recibimos entradas y salidas
    SimonGame(const int entradaPins[4],
              const int salidaPins[4]);

    void update();                   // Una ronda: genera/muestra/lee
    void playFullGame();             // Juega todos los niveles seguidos
    int  getLevel() const;           // Nivel actual
    void stopGame();

private:
    int _entrada[4];
    int _salida[4];

    int _nivelActual;
    int _velocidad;
    int _secuencia[NIVEL_MAX];
    int _secuenciaUsuario[NIVEL_MAX];
    bool _running;

    void generaSecuencia();
    void muestraSecuencia();
    bool leeSecuencia();
    void secuenciaError();
    void secuenciaCorrecta();
};

#endif

SimonGame.cpp

// SimonGame.cpp
#include "SimonGame.h"

// Definición de los nombres
const char* const SimonGame::colorNames[4] = {
    "Red", "Green", "Blue", "Yellow"
};

SimonGame::SimonGame(const int entradaPins[4],
                     const int salidaPins[4])
    : _nivelActual(4),
      _velocidad(500)
{
    for (int i = 0; i < 4; i++) {
        _entrada[i] = entradaPins[i];
        Serial.println(_entrada[i]);
        _salida[i]  = salidaPins[i];
    }
}

void SimonGame::update() {
  if (_running == true) {
    if (_nivelActual == NIVEL_MAX) {
        sendNEXTIONcmd("vaState.val=2");
        Serial.print("Simon game won");
        _running = false;
        return;
    }
    if (_nivelActual == 4) generaSecuencia();
    muestraSecuencia();
    // Renombrada la variable para no chocarnos con la palabra reservada
    leeSecuencia();
  } 
}

void SimonGame::playFullGame() {
    _nivelActual = 4;
    _velocidad   = 500;
    _running = true;
}

int SimonGame::getLevel() const {
    return _nivelActual;
}

void SimonGame::generaSecuencia() {
    randomSeed(millis());
    for (int i = 0; i < NIVEL_MAX; i++) {
        _secuencia[i] = random(0, 4);
    }
}

void SimonGame::muestraSecuencia() {
    for (int i = 0; i < 4; i++) digitalWrite(_salida[i], LOW);

    for (int i = 0; i < _nivelActual; i++) {
        int idx = _secuencia[i];

        // Mostrar en Nextion
        String cmd = String("t0.txt=\"Showing: ");
        cmd += colorNames[idx];
        cmd += "\"";
        sendNEXTIONcmd(cmd.c_str());
        sendNEXTIONcmd("ref t0");

        // Mostrar en Arduino
        Serial.print("Showing: ");
        Serial.println(colorNames[idx]);

        // Encender LED
        digitalWrite(_salida[idx], HIGH);
        delay(_velocidad);
        digitalWrite(_salida[idx], LOW);
        delay(200);
        cmd = String("t0.txt= ");
        sendNEXTIONcmd(cmd.c_str());
        sendNEXTIONcmd("ref t0");
        delay(100);
    }
    String cmd = String("t0.txt=\"Your turn\"");
    sendNEXTIONcmd(cmd.c_str());
    sendNEXTIONcmd("ref t0");
}

bool SimonGame::leeSecuencia() {
    for (int i = 0; i < _nivelActual; i++) {
        bool leido = false;
        while (!leido) {
            for (int btn = 0; btn < 4; btn++) {
                if (digitalRead(_entrada[btn]) == HIGH) {
                    // Mostrar en Nextion
                    String cmd = String("t0.txt=\"You've pressed: ");
                    cmd += colorNames[btn];
                    cmd += "\"";
                    sendNEXTIONcmd(cmd.c_str());
                    sendNEXTIONcmd("ref t0");

                    // Mostrar en Arduino
                    Serial.print("You've pressed: ");
                    Serial.println(colorNames[btn]);

                    // Feedback visual
                    digitalWrite(_salida[btn], HIGH);
                    delay(300);
                    digitalWrite(_salida[btn], LOW);
                    delay(200);

                    _secuenciaUsuario[i] = btn;
                    leido = true;
                    if (_secuenciaUsuario[i] != _secuencia[i]) {
                        secuenciaError();
                        return false;
                    }
                }
            }
        }
    }
    secuenciaCorrecta();
    return true;
}

void SimonGame::secuenciaError() {
    for (int k = 0; k < 2; k++) {
        for (int i = 0; i < 4; i++) digitalWrite(_salida[i], HIGH);
        delay(250);
        for (int i = 0; i < 4; i++) digitalWrite(_salida[i], LOW);
        delay(250);
    }
    _running = false;
    sendNEXTIONcmd("vaState.val=1");
    Serial.println("Simon game lost");
}

void SimonGame::secuenciaCorrecta() {
    if (_nivelActual < NIVEL_MAX) _nivelActual++;
    _velocidad = max(100, _velocidad - 50);
    delay(200);
}

void SimonGame::stopGame(){
  _running = false;
}

DodgeGame.h

#ifndef DODGE_GAME_H
#define DODGE_GAME_H

#include <Arduino.h>
#include "NextionSoftSerial.h"

class DodgeGame {
public:
    // Zonas muertas para no detectar pequeños ruidos
    static const int DEADZONE = 200;

    // Posibles direcciones
    enum Direction { NONE = 0, UP, DOWN, LEFT, RIGHT };

    // Constructor: pines analógicos X e Y del joystick
    DodgeGame(int pinX, int pinY);

    // Arranca el juego (empezar a leer y enviar)
    void startGame();

    // Para el juego (dejar de leer y enviar)
    void stopGame();

    // Si cambia la dirección, la envía a Nextion en t0
    void update();

private:
    int      _pinX, _pinY;
    Direction _lastDir;
    bool      _running;

    // Nombres para mostrar
    static const char* const _dirNames[5];

    // Lectura bruta de joystick -> Direction
    Direction readDirection();
};

#endif // DODGE_GAME_H

DodgeGame.cpp

#include "DodgeGame.h"

// Textos para cada dirección
const char* const DodgeGame::_dirNames[5] = {
    "Ninguna", "Arriba", "Abajo", "Derecha", "Izquierda"
};

DodgeGame::DodgeGame(int pinX, int pinY)
  : _pinX(pinX), _pinY(pinY), _lastDir(NONE), _running(false)
{}

void DodgeGame::startGame() {
    Serial.print("juego iniciado");
    _lastDir = NONE;
    _running = true;
}

void DodgeGame::stopGame() {
    _running = false;
}

DodgeGame::Direction DodgeGame::readDirection() {
    int x = analogRead(_pinX);
    int y = analogRead(_pinY);
    int center = 512;

    int dx = x - center;
    int dy = y - center;

    if (abs(dx) < DEADZONE && abs(dy) < DEADZONE) {
        return NONE;
    }
    if (abs(dx) > abs(dy)) {
        return (dx > 0) ? RIGHT : LEFT;
    } else {
        return (dy > 0) ? UP : DOWN;
    }
}

void DodgeGame::update() {
    if (!_running) return;
    Direction dir = readDirection();
    if (dir != _lastDir) {
        _lastDir = dir;
    
        Serial.print("Joystick apunta a: ");
        Serial.println(_dirNames[dir]);

        // Construir el comando Nextion y enviar como const char*
        String cmd = String("vaUserDir.val=");
        cmd += dir;
        sendNEXTIONcmd(cmd.c_str());
    }
}

Código Nextion

El código Nextion está contenido en el archivo game.hmi y se programa utilizando Nextion Editor. A continuación, presentamos fragmentos relevantes del código para cada minijuego:

Fichero program.s con las variables globales

//The following code is only run once when power on, and is generally used for global variable definition and power on initialization data
//int sys0=0,sys1=0,sys2=0         //At present, the definition of global variable only supports 4-byte signed integer (int), and other types of global quantity declaration are not supported. If you want to use string type, you can use variable control in the page to implement
//int timeoutSCREEN=0
//int nLEN=0
int countdownActive
int timePenalty=0
int countdownLength=900  // 15 minutes
int startTotal,nowTotal,remaining,remMin,remSec
int game1,game2,game3,game4=0
int pinNumber
int retry=0 //Cambia 0
bauds=19200                              // default speed
dim=100
recmod=1//Serial data parsing mode:0-Passive mode;1-Active mode
volume=100
printh 00 00 00 ff ff ff 88 ff ff ff   //Output power on information to serial port
page 0                                         //Power on start page 0

Código para la página inicial page0

Preinicialización de la página
if(retry==0)
{
  vis bStart,1
  vis bRetry,0
  vis t0,0
}else if(retry==1)
{
  vis bStart,0
  vis bRetry,1
  vis t0,1
}

bStart onclick
if(retry==0)
{
  // Set countdown active
  countdownActive=1
  // Calculate time
  startTotal=rtc4*60+rtc5  // rtc4 = minutes, rtc5 = seconds
  pinNumber=rand%10000
  page pageHome
}

bRetry onclick
if(retry==1)
{
  vis t0,0
  vis bRetry,0
  vis bStart,1
  game1=0
  game2=0
  game3=0
  game4=0
  timePenalty=0
  retry=0
}

Código para pageHome

Preinicialización
if(game4==1)
{
  b3.bco=10565
  vaAux.val=pinNumber%10
  covx vaAux.val,b3.txt,0,0
}
if(game3==1)
{
  b2.bco=10565
  vaAux.val=pinNumber%100/10
  covx vaAux.val,b2.txt,0,0
}
if(game2==1)
{
  b1.bco=10565
  vaAux.val=pinNumber%1000/100
  covx vaAux.val,b1.txt,0,0
}
if(game1==1)
{
  b0.bco=10565
  vaAux.val=pinNumber/1000
  covx vaAux.val,b0.txt,0,0
}
//Bomb Timer Init
if(countdownActive==1)
{
  nowTotal=rtc4*60+rtc5
  if(nowTotal<startTotal)
  {
    nowTotal+=3600
  }
  pageHome.vaAux.val=timePenalty*60
  remaining=countdownLength+startTotal-nowTotal-pageHome.vaAux.val
  if(remaining<=0)
  {
    countdownActive=0
    xstr 620,0,180,75,11,63488,6339,1,1,1,"00:00"
    retry=1
    page page0
  }else
  {
    remMin=remaining/60
    remSec=remaining%60
    covx remMin,pageHome.vaStrAux.txt,2,0
    pageHome.vaStrAux.txt+=":"
    covx remSec,pageHome.vaStrAux2.txt,2,0
    pageHome.vaStrAux.txt+=pageHome.vaStrAux2.txt
    xstr 620,0,180,75,11,63488,6339,1,1,1,pageHome.vaStrAux.txt
  }
}

b0 onclick
page pageSimon

b1 onclick
page pageDodge

b2 onclick
page pageCountdown

b3 onclick
page pageCountdown

Código para PageSimon

Variables
vaState=0

Timers
tmCheck
if(vaState.val==1)
{
  bStartStop.txt="Try again"
  vis bStartStop,1
  vis t0,0
  vaState.val=0
  timePenalty++
  vis bBack,1
}else if(vaState.val==2)
{
  t0.txt="Well done"
  game1=1
  vis bBack,1
}

bStartStop onclick
vis t0,1
vis bStartStop,0
printh "STARTGAME1"
vis bBack,0

Código de PageDodge

Variables
vaAuxString=newtxt
vaState=0
b1XSum=10
b1YSum=5
xdir=0
auxNum=0
ydir=0
auxNum2=0
surTime=20
vaLost=0
b2XSum=5
b2YSum=0
xdir2=0
ydir2=0
vaUserDir=0

Timers
tmGame
if(vaState.val==1)
{
  if(vaUserDir.val==2)
  {
    if(player.y>95)
    {
      player.y-=10
    }
  }
  if(vaUserDir.val==1)
  {
    if(player.y<410)
    {
      player.y+=10
    }
  }
  if(vaUserDir.val==3)
  {
    if(player.x>20)
    {
      player.x-=10
    }
  }
  if(vaUserDir.val==4)
  {
    if(player.x<680)
    {
      player.x+=10
    }
  }
  // b1 movement
  if(b1.x<20||b1.x>680||b1.y<95||b1.y>410)
  {
    b1YSum.val=rand
    b1XSum.val=rand
    if(b1.x<20)
    {
      xdir.val=1
      if(rand>10)
      {
        ydir.val=1
      }else
      {
        ydir.val=0
      }
    }
    if(b1.x>680)
    {
      xdir.val=0
      if(rand>10)
      {
        ydir.val=1
      }else
      {
        ydir.val=0
      }
    }
    if(b1.y<95)
    {
      ydir.val=1
      if(rand>10)
      {
        xdir.val=1
      }else
      {
        xdir.val=0
      }
    }
    if(b1.y>410)
    {
      ydir.val=0
      if(rand>10)
      {
        xdir.val=1
      }else
      {
        xdir.val=0
      }
    }
  }
  if(xdir.val==0)
  {
    b1.x-=b1XSum.val
  }else
  {
    b1.x+=b1XSum.val
  }
  if(ydir.val==0)
  {
    b1.y-=b1YSum.val
  }else
  {
    b1.y+=b1YSum.val
  }
  // b2 movement
  if(b2.x<20||b2.x>680||b2.y<95||b2.y>410)
  {
    b2YSum.val=rand
    b2XSum.val=rand
    if(b2.x<20)
    {
      xdir2.val=1
      if(rand>10)
      {
        ydir2.val=1
      }else
      {
        ydir2.val=0
      }
    }
    if(b2.x>680)
    {
      xdir2.val=0
      if(rand>10)
      {
        ydir2.val=1
      }else
      {
        ydir2.val=0
      }
    }
    if(b2.y<95)
    {
      ydir2.val=1
      if(rand>10)
      {
        xdir2.val=1
      }else
      {
        xdir2.val=0
      }
    }
    if(b2.y>410)
    {
      ydir2.val=0
      if(rand>10)
      {
        xdir2.val=1
      }else
      {
        xdir2.val=0
      }
    }
  }
  if(xdir2.val==0)
  {
    b2.x-=b2XSum.val
  }else
  {
    b2.x+=b2XSum.val
  }
  if(ydir2.val==0)
  {
    b2.y-=b2YSum.val
  }else
  {
    b2.y+=b2YSum.val
  }
  // check losing condition
  // check b1
  auxNum.val=b1.x+b1.w
  auxNum2.val=player.x+player.w
  if(auxNum.val>player.x&&b1.x<auxNum2.val)
  {
    auxNum.val=b1.y+b1.h
    auxNum2.val=player.y+player.h
    if(auxNum.val>player.y&&b1.y<auxNum2.val)
    {
      vaLost.val=1
    }
  }
  // check b2
  if(vaLost.val==0)
  {
    auxNum.val=b2.x+b2.w
    if(auxNum.val>player.x&&b2.x<auxNum2.val)
    {
      auxNum.val=b2.y+b2.h
      auxNum2.val=player.y+player.h
      if(auxNum.val>player.y&&b2.y<auxNum2.val)
      {
        vaLost.val=1
      }
    }
  }
  if(vaLost.val==1)
  {
    vaLost.val=0
    vis t1,1
    t1.txt="You lost"
    vaState.val=2
    timePenalty++
    bStartStop.txt="try again"
    vis bStartStop,1
    surTime.val=20
    vis bBack,1
    printh "ENDGAME2"
  }
}

timVictoryCond
if(vaState.val==1)
{
  if(surTime.val==0)
  {
    game2=1
    vis b1,0
    vis b2,0
    vis t1,1
    t1.txt="CONGRATULATIONS!!!"
    vaState.val=3
    vis bBack,1
  }else
  {
    surTime.val--
  }
}

bStartStop onclick
if(vaState.val==2)
{
  b1.x=68
  b1.y=118
  b2.x=596
  b2.y=116
  player.x=376
  player.y=400
  b1YSum.val=rand
  b1XSum.val=rand
  b2YSum.val=rand
  b2XSum.val=rand
  vis bBack,0
  printh "STARTGAME2"
}
vaState.val=1
vis bStartStop,0
vis t1,0
printh "STARTGAME2"

Código de pageCountdown

Preinicialización
//Bomb Timer Init
if(countdownActive==1)
{
  nowTotal=rtc4*60+rtc5
  if(nowTotal<startTotal)
  {
    nowTotal+=3600
  }
  pageHome.vaAux.val=timePenalty*60
  remaining=countdownLength+startTotal-nowTotal-pageHome.vaAux.val
  if(remaining<=0)
  {
    countdownActive=0
    xstr 620,0,180,75,11,63488,6339,1,1,1,"00:00"
    retry=1
    page page0
  }else
  {
    remMin=remaining/60
    remSec=remaining%60
    covx remMin,pageHome.vaStrAux.txt,2,0
    pageHome.vaStrAux.txt+=":"
    covx remSec,pageHome.vaStrAux2.txt,2,0
    pageHome.vaStrAux.txt+=pageHome.vaStrAux2.txt
    xstr 620,0,180,75,11,63488,6339,1,1,1,pageHome.vaStrAux.txt
  }
}

Variables
vaSecond=5
vaCentSecond=0
vaAuxString=newtxt
vaState=0

Timer
tmCount
vaCentSecond.val-=5
if(vaCentSecond.val<0)
{
  vaCentSecond.val=95
  vaSecond.val--
}
// Convert vaSecond to string and store in t1.txt
covx vaSecond.val,t1.txt,2,0
// Add the colon
t1.txt=t1.txt+":"
// Convert vaCentSecond to string and append to t1.txt
covx vaCentSecond.val,vaAuxString.txt,2,0
t1.txt+=vaAuxString.txt
if(vaSecond.val==3)
{
  vis t1,0
}
if(vaSecond.val<0)
{
  tmCount.en=0
  vaState.val=2
  bStartStop.txt="TRY AGAIN"
  t1.txt="00:00"
  t0.txt="YOU ARE A LOSER"
  timePenalty++
  vis t1,1
}


bStartStop onclick
if(vaState.val==0)
{
  vaState.val=1
  bStartStop.txt="STOP"
  tmCount.en=1
  vis bBack,0
}else if(vaState.val==1)
{
  tmCount.en=0
  if(vaSecond.val==0&&vaCentSecond.val<40)
  {
    t1.txt="00:00"
    t0.txt="CONGRATULATIONS!!!"
    game3=1
  }else
  {
    timePenalty++
    t0.txt="YOU ARE A LOSER"
    vaState.val=2
    bStartStop.txt="TRY AGAIN"
  }
  vis bBack,1
  vis t1,1
}else
{
  vaSecond.val=5
  vaCentSecond.val=0
  vaState.val=0
  bStartStop.txt="START"
  t0.txt="STOP AT 00:00"
  t1.txt="05:00"
}

Código pageDontPress

Preinicialización
// Configuración inicial
tm0.tim=30000  // 30 segundos para ganar
tm1.tim=300    // Temporizador de animación (300ms)
tm0.en=1       // Activar temporizador principal
tm1.en=1       // Activar animación
vis b1,0       // Ocultar botón reintentar
vis bBack,0    // Ocultar botón volver
t0.txt="Don't press the red button!"
// Estado inicial del botón
b0.bco=63776   // Rojo claro (no pulsado)
b0.pco=WHITE   // Texto siempre blanco
b0.txt="PUSH ME"
// Bomb Timer Init
if(countdownActive==1)
{
  nowTotal=rtc4*60+rtc5
  if(nowTotal<startTotal)
  {
    nowTotal+=3600
  }
  pageHome.vaAux.val=timePenalty*60
  remaining=countdownLength+startTotal-nowTotal-pageHome.vaAux.val
  if(remaining<=0)
  {
    countdownActive=0
    xstr 620,0,180,75,11,63488,6339,1,1,1,"00:00"
    retry=1
    page page0
  }else
  {
    remMin=remaining/60
    remSec=remaining%60
    covx remMin,pageHome.vaStrAux.txt,2,0
    pageHome.vaStrAux.txt+=":"
    covx remSec,pageHome.vaStrAux2.txt,2,0
    pageHome.vaStrAux.txt+=pageHome.vaStrAux2.txt
    xstr 620,0,180,75,11,63488,6339,1,1,1,pageHome.vaStrAux.txt
  }
}


Timers
tm0
tm1.en=0       // Detener animación
// Efecto de victoria
b0.bco=2016    // Verde
b0.pco=WHITE   // Texto blanco
b0.txt="WINNER!"
t0.txt="Congratulations!!!"
game4=1
tsw b0,0       // Desactivar botón
vis b1,1       // Mostrar reintentar
vis bBack,1    // Mostrar volver

tm1
// Alternar entre estados de pulsación
if(b0.bco==63776)  // Si está en rojo claro (no pulsado)
{
  b0.bco=32768     // Cambiar a rojo oscuro (como pulsado)
}else
{
  b0.bco=63776     // Volver a rojo claro (no pulsado)
}
ref b0  // Refrescar el botón

b0 onclick
tm0.en=0       // Detener temporizador de victoria
tm1.en=0       // Detener animación
// Efecto de derrota
b0.bco=BLACK   // Fondo negro
b0.pco=WHITE   // Texto blanco
b0.txt="OOPS!"
t0.txt="You are a loser!"
timePenalty++
tsw b0,0       // Desactivar botón
vis b1,1       // Mostrar reintentar
vis bBack,1    // Mostrar volver

b1 onclick
// Reiniciar juego
tm0.tim=30000
tm1.tim=300
tm0.en=1
tm1.en=1
// Restaurar estado inicial
b0.bco=64512   // Rojo claro
b0.pco=WHITE   // Texto blanco
b0.txt="PUSH ME"
t0.txt="Don't press the red button!"
tsw b0,1       // Reactivar botón
vis b1,0       // Ocultar reintentar
vis bBack,0    // Ocultar volver

Código PagePin

Preinicialización
pagePin.show.txt=""
// Bomb Timer Init
if(countdownActive==1)
{
  nowTotal=rtc4*60+rtc5
  if(nowTotal<startTotal)
  {
    nowTotal+=3600
  }
  pageHome.vaAux.val=timePenalty*60
  remaining=countdownLength+startTotal-nowTotal-pageHome.vaAux.val
  if(remaining<=0)
  {
    countdownActive=0
    xstr 620,0,180,75,11,63488,6339,1,1,1,"00:00"
    retry=1
    page page0
  }else
  {
    remMin=remaining/60
    remSec=remaining%60
    covx remMin,pageHome.vaStrAux.txt,2,0
    pageHome.vaStrAux.txt+=":"
    covx remSec,pageHome.vaStrAux2.txt,2,0
    pageHome.vaStrAux.txt+=pageHome.vaStrAux2.txt
    xstr 620,0,180,75,11,63488,6339,1,1,1,pageHome.vaStrAux.txt
  }
}

Variables
input=""
pinLEN=4
vaPAGE=1
pinRST=4321
pinCONSOLE=9876
pin=""

bOk onclick
covx pinNumber,pin.txt,4,0
if(show.txt==pin.txt)
{
  show.txt=""
  // pin OK
  page pageWin
}else
{
  // pin  ERROR
  show.txt=""
  input.txt=""
  timePenalty++
}

Temporizador global de la bomba

Todas las páginas tienen este timer común que controla los 15 minutos de juego. Si el usuario falla restará un minuto.

if(countdownActive==1)
{
  nowTotal=rtc4*60+rtc5
  if(nowTotal<startTotal)
  {
    nowTotal+=3600
  }
  pageHome.vaAux.val=timePenalty*60
  remaining=countdownLength+startTotal-nowTotal-pageHome.vaAux.val
  if(remaining<=0)
  {
    countdownActive=0
    xstr 620,0,180,75,11,63488,6339,1,1,1,"00:00"
    retry=1
    page page0
  }else
  {
    remMin=remaining/60
    remSec=remaining%60
    covx remMin,pageHome.vaStrAux.txt,2,0
    pageHome.vaStrAux.txt+=":"
    covx remSec,pageHome.vaStrAux2.txt,2,0
    pageHome.vaStrAux.txt+=pageHome.vaStrAux2.txt
    xstr 620,0,180,75,11,63488,6339,1,1,1,pageHome.vaStrAux.txt
  }
}

También te podría gustar...

Deja una respuesta

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