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:
Componente | Cantidad | Función |
---|---|---|
Arduino MEGA 2560 | 1 | Cerebro del sistema que ejecuta la lógica principal |
Pantalla táctil Nextion NX8048P050-011C-Y | 1 | Interfaz gráfica e interacción con el usuario |
Joystick analógico XY con botón | 1 | Control del personaje en DodgeGame |
Botones con LED integrados | 4 | Interacciones físicas y feedback visual |
Resistencias y cables | Varios | Conexiones y circuitos |
Tabla de cartón pluma | 1 | Base 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
Componente | Cantidad | Precio unitario (€) | Precio total (€) |
---|---|---|---|
Pantalla táctil Nextion NX8048P050-011C-Y | 1 | 80,00 | 80,00 |
Joystick para Arduino (XY con botón) | 1 | 3,00 | 3,00 |
Placa Arduino Mega 2560 (compatible) | 1 | 15,00 | 15,00 |
Cables | 1 set (40 uds) | 4,00 | 4,00 |
Resistencias surtidas | 1 set | 2,00 | 2,00 |
Botones con LED integrados | 5 | 2,00 | 10,00 |
Tabla de cartón pluma (aprox. 50×70 cm) | 1 | 5,00 | 5,00 |
Kit Soldador | 1 | 10,00 | 10,00 |
Total | 129,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
}
}