Please help, code crashes and I can't figure out why

Hello all. Time is getting thin, as is my ability to troubleshoot errors I do not understand.

My code interfaces with an Arduino Uno R4 Wifi and all the Arduino does is send messages over Serial to change the state of my Halloween Laser Maze game.

Any hints, obvious errors or solutions greatly appreciated (I’m just a dad, an Arduino hobbyist and I like to use Halloween as a project showcase and to hopefully get kids interested in coding/electronics).

The crash always seems to occur after about the fourth or fifth time running through the program and the flow works like: waiting state; start button pressed (“start” received by Processing over serial); some or no alarm trips (“alarm” and “off” received by Processing over serial); stop (“stop” rx over serial); reset button pressed (“reset” over serial). I am using Processing 4 and have Java version 8 installed on my Windows 11 PC, Update 461 (build 1.8.0_461-b11).

import processing.sound.*;
import processing.video.*;
import processing.serial.*;
import lord_of_galaxy.timing_utils.*;

Serial myPort;
Movie intro;
Movie bestUnicorn;
Movie green;
Movie cobred;
SoundFile alarm;
SoundFile Hydra;
SoundFile[] currentSong = new SoundFile[6];
Stopwatch stopwatch1;
Stopwatch countdown; // 3,2,1 counter clock
Stopwatch gameTimer; // general game timer
PFont font;
PFont scoreFont;
PImage mars;
PImage cobra;
PImage destro;
String[] com = {"COM2", "COM3", "COM4",
  "COM5", "COM6", "COM7", "COM8", "COM9", "COM10", "COM11",
  "COM12", "COM13", "COM14", "COM15", "COM16", "COM17", "COM18",
  "COM19", "COM20", "COM21", "COM22", "COM23", "COM24", "COM25", "COM26"};

// for the 3D rotating cubes
float rotx = PI/4;
float roty = PI/4;
float rate = 0.0001;
boolean overButton = false;
int clicker;
int clickCounter;
boolean introStarted;
float grn;  // green grid color in normal
float red; // red grid color in cobra
int i;              //
float timerStart;   // players starting time
float timerFour;
String timerTotal;
int score;          // players score
int gameStage;          // controls state of the game and what is displayed on screen
int yourScore; // player's final calculated score
int yourTopScore; // top score of the day
int yourTime;
int yourFactor;
int todaysHighScore = 0;
float milliseconds = 0.0;
float seconds = 0.0;
float timeWarpVal = 10000.0;   // used in player elapsed time calc.
float normalTimeVal = 1000.0; // used in player elapsed time calc.
float currentTimeVal = 0.0;
float gameTimeState = 0.0;   // holds val of time modifiers
float audioBottomRate = 0.1;
float audioTopRate = 1.0;
float fontSize = 19;

boolean alreadyUsedSpell;
boolean isSlowed; // prevent slowMusic(); from repeating during bubbles/unicorn
boolean isDestro; // when alarm trigg over webserver, destro cube and when reset over webserver, mars
boolean readyArduino = false; // toggle with 'p' 'P' to let Arduino know where Processing game is (no trigs during gman, for ex)

int timesReset = 0;
int gameCounter = 0; // keeps track of how many games have been played to update soundtrack
int trackCount = 0;
float randTrack;

void settings() {
  // fullScreen(P3D);
  size(1920, 1080, P3D); // run on native 1080p monitor. Check Preferences to match. Otherwise might cause Nvidia error nvoglv64.dll?
}

void setup() {
  background(0, 59, 0);
  noStroke();
  fill(204);
  stopwatch1 = new Stopwatch(this);
  countdown = new Stopwatch(this);
  gameTimer = new Stopwatch(this);
  font = createFont("ethnocentric rg.ttf", 70);
  scoreFont = loadFont("MakeMeACyborg-48.vlw");
  textAlign(CENTER);
  imageMode(CENTER);
  textFont(font, 40);
  fill(0, 200, 0);
  println("loading audio");
  alarm = new SoundFile(this, "burglar.wav");
  Hydra = new SoundFile(this, "Hydra.wav");
  currentSong[0] = new SoundFile(this, "Fresh Action Groove B.wav");     //3:45 - 225 sec
  currentSong[1] = new SoundFile(this, "Smart Cookies.wav");    //2:13 - 133 sec + last = 359
  currentSong[2] = new SoundFile(this, "Robots At Work.wav");   //2:34 - 154 sec + last = 513
  currentSong[3] = new SoundFile(this, "Secret Operation.wav"); //2:59 - 179 sec + last = 693
  currentSong[4] = new SoundFile(this, "Metaphysics.wav");      //3:43 - 223 sec + last = 917
  currentSong[5] = new SoundFile(this, "Martini.wav");          //4:48 - 288 sec + last = 1206

  println("audio loaded.");
  println("loading movies");
  green = new Movie(this, "green.mp4");
  cobred = new Movie(this, "red.mp4");
  intro = new Movie(this, "laser_maze.mp4");
  bestUnicorn = new Movie(this, "bestUnicorn.mp4");
  println("movies loaded.");
  cobra = loadImage("logo.jpg");
  mars = loadImage("mars.png");
  destro = loadImage("pdDestro.jpg");
  connect(1, 24, 200); // 25 com ports*4 port numbers (buses)*2 tries each
  gameStage = 0;
}

void draw() {
  background(0); // necessary so everything doesn't draw on top of itself over and over
  switch (gameStage) {
  case 0:
    gameReset();
    gameStage = 10;
    break;
  case 10:
    checkMusic();
    setMediaDuringGameplay();
    gameStage = 20;
    break;
  case 20:
    gameStage = 100;
    gameTimer.reset();
    break;
  case 100:
    drawGreen();
    displaySplashScreen(); // instruction to press start and high score display
    checkMusic();
    if (isDestro == false)drawMars();
    else if (isDestro == true) drawDestro();
    break;
    // play the Gman intro Movie
  case 200:
    drawGreen();
    textAlign(CENTER);
    textFont(font, 48);
    fill(0, 80, 0);
    text("press START to skip", width/2, height*0.95);
    currentSong[trackCount].amp(0.2);
    if ((intro.time() >= 15.0)&&(intro.time() <= 22.0)) {
      currentSong[trackCount].amp(0.0);
    }
    if (!intro.isPlaying()) {
      intro.play();
      textFont(font, 48);
    } else if (intro.time() == intro.duration()) {
      intro.stop();
      gameStage = 250;
    } else {
      drawGman();
    }
    checkASCII();
    break;
  case 250:
    setMediaDuringGameplay();
    currentSong[trackCount].amp(0.4);
    countdown.start();
    if (readyArduino == false) {
      myPort.write('P');
      readyArduino = true;
    }
    gameStage = 300;
    break;
    // 3, 2, 1, GO
  case 300:
    drawGreen();
    showCountdown();
    // start the actual gameplay
    if (countdown.second() == 5) {
      gameStage = 350;
    }
    break;
  case 350:
    countdown.reset();
    if (readyArduino == false) {
      myPort.write('P'); // let arduino know it's ok to register alarms
      readyArduino = true;
    }
    gameTimer.start();
    gameStage = 390;
    break;
  case 390: // called at end of "regtime" serial event to make sure music retures after unicorn spell
    setMediaDuringGameplay(); // reset music before we leave
    gameStage = 395;
    break;
  case 395:
    gameStage = 400;
    break;
  case 400:
    checkMusic();
    currentSong[trackCount].amp(0.8);
    drawGreen();
    showRunningGame();
    if (isDestro == false) {
      drawMarsButton();
    } else if (isDestro == true) {
      drawDestroButton();
    }
    checkASCII();
    // unicorn magic time
    break;
  case 500:
    drawGreen();
    checkMusic();
    if (!bestUnicorn.isPlaying()) {
      bestUnicorn.loop();
    }
    bubblesTransition();
    if (isSlowed == false) {
      slowMusic();
      isSlowed = true;
    }
    drawUnicorn();
    alreadyUsedSpell = true; // set flag so one spell per game only
    break;
  case 670:
    alarm();
    gameStage = 680;
    break;
  case 680:
    panic();
    break;
  case 690:
    if (readyArduino == true) {
      myPort.write('p'); // let arduino know it's ok to register alarms
      readyArduino = false;
      gameStage = 700;
    }
    break;
  case 700:// end of game - show final score
    checkMusic();
    if (score <= 3) {
      drawGreen();
      showFinalScoreG();
      if (isDestro == false)drawMars();
      else if (isDestro == true) drawDestro();
    } else if (score >= 4) {
      cobra();
      showFinalScoreR();
      drawCobra();
    }
    break;
  case 800:
    cobra();
    break;
    // end of switch gameStage
  }
}
// 0 exits to 10
// resets game variables to defaults for new game
void gameReset() {
  score = 0;
  clicker = 0;
  clickCounter = 0;
  i = 3;
  currentTimeVal = 1000.0;
  introStarted = false;
  alreadyUsedSpell = false;
  isSlowed = false; // set flag so this only happens once per game
  isDestro = false;
  textFont(font, 120);
  fill(0, 200, 0);

  if (intro.isPlaying()) {
    intro.stop();
  }
  if (cobred.isPlaying()) {
    cobred.stop();
  }
  if (green.isPlaying()) {
    green.stop();
  }
  if (alarm.isPlaying()) {
    alarm.pause();
  }
  if (Hydra.isPlaying()) {
    Hydra.pause();
  }
}

void displaySplashScreen() {
  textSize(60);
  textAlign(CENTER);
  fill(#EDE902);
  text("PRESS START", width/2, height/6);
  textAlign(LEFT);
  text("High Score", (width/4), height * 0.85);
  if (todaysHighScore >= yourTopScore) {
    textAlign(RIGHT);
    text(todaysHighScore, (width/4)*3.30, height* 0.85);
  } else {
    text(yourTopScore, (width/4) * 3.30, height* 0.85);
    todaysHighScore = yourTopScore;
  }
}
void setMediaDuringGameplay() {
  if (intro.isPlaying()) {
    intro.stop();
  }
  if (Hydra.isPlaying()) {
    Hydra.pause();
  }
  if (alarm.isPlaying()) {
    alarm.pause();
  }

  tint(255);
  if (!green.isPlaying()) {
    green.play();
    image(green, width/2, height/2);
  }
  if (green.time() > 28.0) {
    green.jump(0);
  }
}
void showCountdown() {
  textSize(280);
  textAlign(CENTER);
  fill(#EDE902); // gold
  if (countdown.second() == 1) {
    text("3", width/2, height/1.7);
  } else if (countdown.second() == 2) {
    text("2", width/2, height/1.7);
  } else if (countdown.second() == 3) {
    text("1", width/2, height/1.7);
  } else if (countdown.second() == 4) {
    text("GO!", width/2, height/1.7);
  }
}
void showRunningGame() {
  textAlign(CENTER);
  textFont(font, 140); // default 120
  fill(#EDE902); // gold
  text("time: ", (width/3.2), height/2 - 80);
  text(str(gameTimer.time()/100), (width/1.5), height/2 -80);
 // text (nf(gameTimer.minute(), 2) + ":" + nf(gameTimer.second(), 2), width/1.5-150, height/2 -80);
 // text (nf(gameTimer.millis()/10, 3), width - 280, height/2 -80);
  text("alarms: ", (width/3.5), height /2 + 140);
  text(score, (width/1.5), height/2 + 140);
}
void bubblesTransition() {
  strokeWeight(10);
  fill(255, 10);
  noStroke();
  rect(width/2, height/2, width, height);
  float radius = random(0, 100);
  float red = random(0, 255);
  float green = random(0, 255);
  float blue = random(0, 255);
  float alpha = random(0, 255);
  stroke(red, green, blue, alpha);
  fill(255-red, 255-green, 255-blue, alpha);
  ellipse(random(0, height), random(0, width), radius * 2, radius * 2);
  textAlign(CENTER);
  textFont(font, 80);
  fill (0, 200, 0);
  text("time", (width/4), height/2 - 400);
  text(str(gameTimer.time()/100), (width/4 + 900), height/2 -400);
  text("alarms", (width/4 -100), height/2 +400);
  text(score, (width/4 + 900), height/2 + 400);
}
// 670 exits to 680
void alarm() {
  if (alarm.isPlaying()) {
    if (alarm.position() == alarm.duration()) {
      alarm.jumpFrame(0);
    }
  } else if (!alarm.isPlaying()) {
    alarm.play();
    alarm.amp(1);
  }
}
// 680 exits back to 400
void panic() {
  if (intro.isPlaying()) {
    intro.stop();
  }
  if (Hydra.isPlaying()) {
    Hydra.pause();
  }
  if (!currentSong[trackCount].isPlaying()) {
    currentSong[trackCount].play();
    currentSong[trackCount].amp(0.7);
  }
  if (alarm.isPlaying()) {
    if (alarm.position() == alarm.duration()) {
      alarm.jumpFrame(0);
    }
  } else if (!alarm.isPlaying()) {
    alarm.play();
    alarm.amp(1);
  }
  textAlign(CENTER);
  fill(212, 175, 55);
  textFont(font, 166);
  text("!! INTRUDER !!", width/2, height/1.98);
  fill(0, 0, 0);
  textFont(font, 164);
  text("!! INTRUDER !!", width/2, height/2);
  fill(0, 0, 0);
  textFont(font, 164 );
  text("!! INTRUDER !!", width/2, height/2.02);
  strokeCap(SQUARE);
  strokeWeight(random(width/100, (width/100)*2));
  float red = random(0, 50);
  if (red < 1 || currentSong[trackCount].isPlaying()) red = random(0, 255);
  stroke(red, 0, 0);
  float x = random(0, width);
  line(x, 0, x, height);
  float csize = random(90, 100);
  fill(clickCounter);
  circle(width*0.9, height/6, csize);
  fill(clickCounter);
  circle(width*0.9, height*0.8, csize);
  fill(clickCounter);
  circle(width/9, height/6, csize);
  fill(clickCounter);
  circle(width/9, height*0.8, csize);
}
void showFinalScoreG() {
  currentSong[trackCount].amp(0.5);
  textSize(60);
  textAlign(CENTER);
  fill(#EDE902); // gold
  text("time: ", (width/3.2), height/2 - 360);
  text(str(gameTimer.time()/100), (width/1.5), height/2 -360);
  text("alarms: ", (width/3.2), height/2 - 260);
  text(score, (width/1.5), height/2 - 260);
  yourScore = score + 1;
  yourFactor = gameTimer.time()/100 * yourScore;
  yourScore = 1000000 - yourFactor;
  text("Your Score: ", (width/3.2), height/2 + 330);
  text(yourScore, (width/1.5), height/2 + 330);
  yourTopScore = yourScore;
  text("High Score", (width/3.2), height/2 +430);
  if (todaysHighScore >= yourTopScore) {
    textAlign(RIGHT);
    text(todaysHighScore, (width/1.5), height/2 + 430);
  } else {
    text(yourTopScore, (width/4) * 3.30, height* 0.85);
    todaysHighScore = yourTopScore;
  }
  textAlign(CENTER);
  textFont(font, 48);
  fill(0, 100, 0);
  text("press RESET button", width/2, height*0.98);
}
void showFinalScoreR() {
  currentSong[trackCount].amp(0.5);
  textSize(60);
  textAlign(CENTER);
  fill(#EDE902); // gold
  text("time: ", (width/3.2), height/2 - 360);
  text(str(gameTimer.time()/100), (width/1.5), height/2 -360);
  text("alarms: ", (width/3.2), height/2 - 260);
  text(score, (width/1.5), height/2 - 260);
  yourScore = score + 1;
  yourFactor = gameTimer.time()/100 * yourScore;
  yourScore = 1000000 - yourFactor;
  text("Your Score: ", (width/3.2), height/2 + 330);
  text(yourScore, (width/1.5), height/2 + 330);
  yourTopScore = yourScore;
  text("High Score", (width/3.2), height/2 +430);
  if (todaysHighScore >= yourTopScore) {
    textAlign(RIGHT);
    text(todaysHighScore, (width/1.5), height/2 + 430);
  } else {
    text(yourTopScore, (width/4) * 3.30, height* 0.85);
    todaysHighScore = yourTopScore;
  }
  textAlign(CENTER);
  textFont(font, 48);
  fill(#EDE902);
  text("press RESET button", width/2, height*0.98);
}
void drawGreen() {
  tint(255);
  if (!green.isPlaying()) {
    green.play();
  }
  image(green, width/2, height/2);
  if (green.time() > 28.0) {
    green.jump(0);
  }
}
void cobra() {
  if (currentSong[trackCount].isPlaying()) {
    currentSong[trackCount].pause();
  }
  if (!Hydra.isPlaying()) {
    Hydra.loop();
    Hydra.amp(0.7);
  }
  if (!cobred.isPlaying()) {
    cobred.play();
  }
  image(cobred, width/2, height/2);
  if (cobred.time() > 28.0) {
    cobred.jump(0);
  }
}
void drawMars() {
  textureMode(NORMAL);
  noStroke();
  directionalLight(5, 255, 28, width/5, height*5, 195);
  pointLight(255, 255, 255, width/4, height*4, 200);
  pointLight(255, 255, 255, width*4, height/4, 200);
  pointLight(255, 255, 255, width/4, height/4, 205);
  pointLight(255, 255, 255, width*4, height*4, 205);
  ambientLight(255, 255, 255);
  translate(width/2.0, height/2.0, 200); // 1080p
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(140);
  tint(255, 166);
  MarsCube(mars);
}
void drawCobra() {
  noStroke();
  translate(width/2.0, height/2.0, 200); // -1000?
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(140);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, height/2.1, 0);
  pointLight(129, 129, 129, width/1.9, height*1.5, 0);
  pointLight(129, 129, 129, width/2.1, height/2.0, 0);
  //  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  emissive(129, 0, 0);
  tint(255, 176);
  // specular(255, 255, 255);
  CobraCube(cobra);
}
void drawDestro() {
  noStroke();
  translate(width/2.0, height/2.0, 200); // -1000?
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(140);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, height/2.1, 0);
  pointLight(129, 129, 129, width/1.9, height*1.5, 0);
  pointLight(129, 129, 129, width/2.1, height/2.0, 0);
  //  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  emissive(129, 0, 0);
  tint(255, 176);
  // specular(255, 255, 255);
  DestroCube(destro);
}
void drawMarsButton() {
  textureMode(NORMAL);
  noStroke();
  translate(width/2, 820, 80);
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(80);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, 910, 0);
  pointLight(129, 129, 129, width/1.9, 890, 0);
  pointLight(129, 129, 129, width/2.1, 930, 0);
  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  tint(255, 166);
  MarsCube(mars);
}
void drawCobraButton() {
  textureMode(NORMAL);
  noStroke();
  translate(width/2, 800, 80);
  translate(width/2, 820, 80);
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(80);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, 910, 0);
  pointLight(129, 129, 129, width/1.9, 890, 0);
  pointLight(129, 129, 129, width/2.1, 930, 0);
  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  emissive(129, 0, 0);
  tint(255, 176);
  specular(255, 255, 255);
  CobraCube(cobra);
}
void drawDestroButton() {
  textureMode(NORMAL);
  noStroke();
  translate(width/2, 820, 80);
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(80);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, 910, 0);
  pointLight(129, 129, 129, width/1.9, 890, 0);
  pointLight(129, 129, 129, width/2.1, 930, 0);
  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  tint(255, 166);
  DestroCube(destro);
}
void drawGman() {
  textureMode(NORMAL);
  noStroke();
  lightSpecular(255, 255, 255);
  pointLight(255, 255, 255, width/4, height*4, 200);
  pointLight(255, 255, 255, width*4, height/4, 200);
  pointLight(255, 255, 255, width/4, height/4, 205);
  pointLight(255, 255, 255, width*4, height*4, 205);
  translate(width/2.0, height/2.0, 200); // 1080p
  roty += frameRate*0.00008;
  rotateY(roty);
  scale(220);
  tint(255, 186);
  GCube(intro);
}
void drawUnicorn() {
  textureMode(NORMAL);
  noStroke();
  lightSpecular(255, 255, 255);
  pointLight(255, 255, 255, width/4, height*4, 200);
  pointLight(255, 255, 255, width*4, height/4, 200);
  pointLight(255, 255, 255, width/4, height/4, 205);
  pointLight(255, 255, 255, width*4, height*4, 205);
  translate(width/2.0, height/2.0, 200); // 1080p
  roty += frameRate*0.00008;
  rotateY(roty);
  scale(220); // DEFAULT 180
  tint(255, 166);
  uniCube(bestUnicorn);
}
void MarsCube(PImage mars) {
  beginShape(QUADS);
  texture(mars);
  getVertices();
  endShape();
}
void CobraCube(PImage cobra) {
  beginShape(QUADS);
  texture(cobra);
  getVertices();
  endShape();
}
void DestroCube(PImage destro) {
  beginShape(QUADS);
  texture(destro);
  getVertices();
  endShape();
}
void GCube(Movie intro) {
  beginShape(QUADS);
  texture(intro);
  getVertices();
  endShape();
}
void uniCube(Movie bestUnicorn) {
  beginShape(QUADS);
  texture(bestUnicorn);
  getVertices();
  endShape();
}
void movieEvent(Movie m) {
  if (m == intro) {
    intro.read();
  } else if (m == green) {
    green.read();
  } else if (m == cobred) {
    cobred.read();
  } else if (m == bestUnicorn) {
    bestUnicorn.read();
  }
}
void printSerialMenu() {
  println("========== laserMaze2025_v1_0 =========");
  println("== comp: laserMaze2025DualMode_v1.0 ===");
  println("========== MENU =========");
  println("d/D ZERO alarmSystemArmed");
  println("a/A SET alarmSystemArmed");
  println("b/B ON NETWORK toggle hardMode");
}
void checkASCII() {
  if (keyPressed) {
    if (key == '?') {
      println("gameStage: " + gameStage);
      println("isDestro: " + isDestro);
      println("readyArduino: " + readyArduino);
      println("isSlowed: " + isSlowed);
      println("alreadyUsedSpell: " + alreadyUsedSpell);
      println("timesReset: " + timesReset);
    } else if (key == 'm') {
      printSerialMenu();
    }
    delay(160);
  }
}

void checkMusic() {
  if (!currentSong[trackCount].isPlaying()) {
    trackCount = (trackCount + 1) % currentSong.length;
    currentSong[trackCount].play();
    gameCounter = trackCount;
    currentSong[trackCount].amp(0.7);
  }
}
void slowMusic() {
  //slow the music down
  currentSong[trackCount].rate(1.0);
  float thisRate = audioTopRate;
  for (float i = 0.0; i <= 80.0; i++) {
    thisRate -= 0.01;
    currentSong[trackCount].rate(thisRate);
    delay(50);
    // println(thisRate);
  }
  currentSong[trackCount].rate(0.1);
}
void speedMusic() {
  currentSong[trackCount].jump(3.5);
  currentSong[trackCount].rate(0.1);
  // speed the music back up
  for (float i = 0.1; i <= 9; i++) {
    currentSong[trackCount].rate(audioBottomRate + i);
    delay(150);
  }
  currentSong[trackCount].rate(1.0);
}
// returns your float truncated to tenths position for time calc
float truncate(float x) {
  if ( x > 0 )
    return float(floor(x * 10))/10;
  else
    return float(ceil(x * 10))/10;
}
void getVertices() {
  // +Z "front" face
  vertex(-1, -1, 1, 0, 0);
  vertex( 1, -1, 1, 1, 0);
  vertex( 1, 1, 1, 1, 1);
  vertex(-1, 1, 1, 0, 1);
  // -Z "back" face
  vertex( 1, -1, -1, 0, 0);
  vertex(-1, -1, -1, 1, 0);
  vertex(-1, 1, -1, 1, 1);
  vertex( 1, 1, -1, 0, 1);
  // +Y "bottom" face
  vertex(-1, 1, 1, 0, 0);
  vertex( 1, 1, 1, 1, 0);
  vertex( 1, 1, -1, 1, 1);
  vertex(-1, 1, -1, 0, 1);
  // -Y "top" face
  vertex(-1, -1, -1, 0, 0);
  vertex( 1, -1, -1, 1, 0);
  vertex( 1, -1, 1, 1, 1);
  vertex(-1, -1, 1, 0, 1);
  // +X "right" face
  vertex( 1, -1, 1, 0, 0);
  vertex( 1, -1, -1, 1, 0);
  vertex( 1, 1, -1, 1, 1);
  vertex( 1, 1, 1, 0, 1);
  // -X "left" face
  vertex(-1, -1, -1, 0, 0);
  vertex(-1, -1, 1, 1, 0);
  vertex(-1, 1, 1, 1, 1);
  vertex(-1, 1, -1, 0, 1);
}
boolean connect(int portNumber, int numCom, int limit) {
  background (0, 14, 255);
  int tries = 0;
  while (tries < limit) {
    try {
      printArray (Serial.list()[portNumber]);
      myPort = new Serial(this, com[numCom], 115200);
    }
    catch(Exception e) {
      System.err.println("Retrying " + "Port " + com[numCom] + " on Port Bus " + portNumber);
      // numCom -= 1;
      numCom += 1;
      tries++;
      if (numCom < 0) { // prevent ArrayIndexOutOfBoundsException
        numCom = 24;
      } else if (numCom > 24) { // prevent ArrayIndexOutOfBoundsException
        numCom = 0;
      }
      if (tries % 25 == 0) {
        portNumber += 1;
        if (portNumber > 4) {
          portNumber = 0;
        }
      }
      delay(200);
      continue;
    }
    break;
  }
  if (tries < limit) {
    println("Connected in " + str(tries + 1) + " tries.");
    background(0, 255, 0);
    delay(200);
    background(0);
    return true;
  } else {
    System.err.println("Connection failed.");
    return false;
  }
}
void serialEvent (Serial myPort) {
  String inString = myPort.readStringUntil('\n');
  if (inString != null) {
    inString = trim(inString);
    if (gameStage == 400) {
      if (inString.equals("alarm")) {
        println("alarm");
        score+=1;
        gameStage = 680;
      }
    }
    if (inString.equals("off")) {
      println("off");
      gameStage = 400; // return to reg play
    }
    if (inString.equals("start")) {
      println("start");
      if (gameStage == 100) {
        gameStage = 200;
      } else if (gameStage == 200) { // skips video midway if desired
        gameStage = 250;
      }
    }
    if (inString.equals("stop")) {
      if (gameStage == 400 || gameStage == 500 || gameStage == 670 || gameStage == 680) {
        println("stop");
        gameTimer.pause();
        gameCounter++;
        gameStage = 690;
      }
    }
    if (inString.equals("reset")) {
      if (gameStage != 0) {
        println("reset");
        if (readyArduino == true|| readyArduino == false) {
          myPort.write('p');
          readyArduino = false;
        }
        timesReset ++;
        if (!currentSong[trackCount].isPlaying()) {
          currentSong[trackCount].stop();
        }
        gameReset();
        gameStage = 10;
      }
    }
    if (inString.equals("timewarp")) {
      if (alreadyUsedSpell == false) { // toggle in gameStage state machine
        if (gameStage == 400) {
          println("timewarp");
          gameTimer.pause();
          currentSong[trackCount].amp(1.0);
          gameStage = 500;
        }
      }
    }
    if (inString.equals("regtime")) {
      println("regtime");
      gameTimer.restart();
      currentTimeVal = 1000.0;
      speedMusic();
      gameStage = 390; // checks and resets music if needed
    }
    // check for remote Destro cube swap trigger
    if (inString.equals("destro")) {
      println("destro");
      isDestro = true;
    }
    if (inString.equals("mars")) {
      println("mars");
      isDestro = false;
    }
  }
}

Here is the error:

# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffafa700a9f, pid=71468, tid=58180
#
# JRE version: OpenJDK Runtime Environment Temurin-17.0.8+7 (17.0.8+7) (build 17.0.8+7)
# Java VM: OpenJDK 64-Bit Server VM Temurin-17.0.8+7 (17.0.8+7, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# Problematic frame:
# C  [nvoglv64.dll+0xb20a9f]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\Users\colto\Documents\Processing\hs_err_pid71468.log
#
# If you would like to submit a bug report, please visit:
#   https://github.com/adoptium/adoptium-support/issues
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
Could not run the sketch (Target VM failed to initialize).
Make sure that you haven't set the maximum available memory too high.
For more information, read Help ? Troubleshooting.

It doesn’t seem to matter what I set the memory to in Preferences, it’s either too low (out of memory) so I increase it as much as needed but the same crash happens

Hello @Hallowed31 ,

This suggests it is related to your NVIDIA drivers.
Try updating them.

Processing 4.
Which version? The latest is Processing 4.4.10
Sometimes a version can break things or fix things!

Java 8.
Processing uses it’s own bundled version and the error report shows the version.
This should not affect your Processing project.

The Asking Questions Guidelines here can be very helpful!
Welcome to the Processing Foundation Discourse

When did you start seeing the error?
I build my projects in stages, save a working version with comments and then start the next version.

:)

2 Likes

Hello again!

I could not run your code as is!

Testing:

  • Replaced all images, videos and wav files.
  • Commented out custom fonts.
  • Commented out missing references to classes.
  • Commented out import that was not provided.

I rarely do this but was curious and only took a few moments for me.

It is a good example of simulating input for testing.

I was able to replace serial input with simulate data:

// Array of valid command strings
String[] validCommands = {"alarm", "off", "start", "stop", "reset", "timewarp", "regtime", "destro", "mars"};

// Array of gameStages that correspond to each command
int[] validGameStages = {400, 400, 100, 400, 0, 400, 400, 400, 400};  

int serialSub(int i)
  {   
  return validGameStages[i];
  }
  
int ch = 0;
int counter = 0;
  
void keyPressed()
  {
  ch++;
  ch = ch%9;
  gameStage = serialSub(ch);
  println(counter, ch, validGameStages[ch], validCommands[ch] );
  counter++;
  }

Seems to work if I hit keys slowly.

It crashes if I hit keys to quickly with same error as yours:

You may be changing states too quickly!

Give this a try:

void gameReset() {
  //score = 0;
  //clicker = 0;
  //clickCounter = 0;
  //i = 3;
  //currentTimeVal = 1000.0;
  //introStarted = false;
  //alreadyUsedSpell = false;
  //isSlowed = false; // set flag so this only happens once per game
  //isDestro = false;
  ////textFont(font, 120);
  //fill(0, 200, 0);

  //if (intro.isPlaying()) {
  //  intro.stop();
  //}
  //if (cobred.isPlaying()) {
  //  cobred.stop();
  //}
  //if (green.isPlaying()) {
  //  green.stop();
  //}
  //if (alarm.isPlaying()) {
  //  alarm.pause();
  //}
  //if (Hydra.isPlaying()) {
  //  Hydra.pause();
  //}
  setup(); // Starts over!
}

I did comment this so above may have to be consideration when connecting to COM ports again (easy fix with a boolean variable) :
//connect(1, 24, 200); // 25 com ports*4 port numbers (buses)*2 tries each

Keep at it!

:)

1 Like

Awesome, thanks for taking the time!

Sorry bout that. It has files and stuff in the data folder but you already know that (just saying for anyone else who comes across this thread).

and the rest of your suggestions: very slick. I admit your skill is beyond my brute force approach. I have no education or training in anything related to coding or electronics at all; I’m just a dad and Hallowe’en enthusiast. Thanks and i learned some from your example.

Came down more to not binding serial triggers to Processing very neatly. Maybe I was recursively calling the same files and crashing the program or maybe one wasn’t closing fast enough before the other began, or maybe it’s to do with the Serial class in the new Arduino Uno R4 wifi Renesas chip that I have read doesn’t work the same way as the ATMega328P from the Uno R3.

So, yes, in part. I revised the code where necessary to prevent accidental (or intentional given the intended users - kids at Hallowe’en) button spamming. I also added a char trigger from Processing to Arduino to let it know if the app has just launched or relaunched, so the Arduino resets to its gameState = 0; and is thus on the same page.

Your suggestion to just recall setup(); I considered but ultimately, there are other things going on that I don’t want to mess with there, plus it has to be quick and the way I have it is my style (for better or worse) throughout all my Hallowe’en “parlour” arcade-type games.

I did after you suggested it. Did it fix anything? Well, it didn’t hurt anything so…

In the end, it seemed to be a problem with not closing up audio files or movies when the Serial triggers came in from the Arduino, that was causing the very frequent crashes.

Thanks again for taking a look! There’s still a few days before the we’en so if you happen to have two Arduino Uno R4 wifis, a few pushbuttons, a TSOP38238 IR receiver, a few lasers (I bought some from Amazon but have used holiday decorative ones that I hacked to emit a straight green beam), and a few relays, I can say that this project is a favorite among the trick or treaters, which is why I keep making different versions and running it annually.

Revised Processing code:

/* v1_7: Oct 25, 2025 12:56

Just a single tab version of 1.5 in Processing 4.4.10 for export to app
Added char 'L' trigger in setup() to let Arduino know Processing
  just launched/relaunched so Arduino resets states to initial ones
  
Overall stability seems ok for now. Fix seems to be really dialing in
Serial states and closing up files when not using.

Many functions adapted/taken from the open source work of others.

Hallowed31
*/

import processing.sound.*;
import processing.video.*;
import processing.serial.*;
import lord_of_galaxy.timing_utils.*;

Serial myPort;
Movie intro;
Movie bestUnicorn;
Movie green;
Movie cobred;
SoundFile alarm;
SoundFile Hydra;
SoundFile[] currentSong = new SoundFile[6];
Stopwatch stopwatch1;
Stopwatch countdown; // 3,2,1 counter clock
Stopwatch gameTimer; // general game timer
PFont font;
PFont scoreFont;
PImage mars;
PImage cobra;
PImage destro;
String[] com = {"COM2", "COM3", "COM4",
  "COM5", "COM6", "COM7", "COM8", "COM9", "COM10", "COM11",
  "COM12", "COM13", "COM14", "COM15", "COM16", "COM17", "COM18",
  "COM19", "COM20", "COM21", "COM22", "COM23", "COM24", "COM25", "COM26"};

// for the 3D rotating cubes
float rotx = PI/4;
float roty = PI/4;
float rate = 0.0001;
boolean introStarted;
float grn;  // green grid color in normal
float red; // red grid color in cobra
int i;              //
float timerStart;   // players starting time
float timerFour;
String timerTotal;
int score;          // players score
int gameStage;          // controls state of the game and what is displayed on screen
int yourScore; // player's final calculated score
int yourTopScore; // top score of the day
int yourTime;
int yourFactor;
int todaysHighScore = 0;
float milliseconds = 0.0;
float seconds = 0.0;
float timeWarpVal = 10000.0;   // used in player elapsed time calc.
float normalTimeVal = 1000.0; // used in player elapsed time calc.
float currentTimeVal = 0.0;
float gameTimeState = 0.0;   // holds val of time modifiers
float audioBottomRate = 0.1;
float audioTopRate = 1.0;
float fontSize = 19;

boolean alreadyUsedSpell;
boolean isSlowed; // prevent slowMusic(); from repeating during bubbles/unicorn
boolean isDestro; // when alarm trigg over webserver, destro cube and when reset over webserver, mars
boolean readyArduino = false; // toggle with 'p' 'P' to let Arduino know where Processing game is (no trigs during gman, for ex)

int timesReset = 0;
int gameCounter = 0; // keeps track of how many games have been played to update soundtrack
int trackCount = 0;
float randTrack;

void settings() {
  //fullScreen(P3D);
 size(1920, 1080, P3D); // run on native 1080p monitor. Check Preferences to match. Otherwise might cause Nvidia error nvoglv64.dll?
 // pixelDensity(1);
}

void setup() {
  background(0, 59, 0);
  noStroke();
  fill(204);
  stopwatch1 = new Stopwatch(this);
  countdown = new Stopwatch(this);
  gameTimer = new Stopwatch(this);
  font = createFont("ethnocentric rg.ttf", 70);
  scoreFont = loadFont("MakeMeACyborg-48.vlw");
  textAlign(CENTER);
  imageMode(CENTER);
  textFont(font, 40);
  fill(0, 200, 0);
  println("loading audio");
  alarm = new SoundFile(this, "burglar.wav");
  Hydra = new SoundFile(this, "Hydra.wav");
  currentSong[0] = new SoundFile(this, "Fresh Action Groove B.wav");     //3:45 - 225 sec
  currentSong[1] = new SoundFile(this, "Smart Cookies.wav");    //2:13 - 133 sec + last = 359
  currentSong[2] = new SoundFile(this, "Robots At Work.wav");   //2:34 - 154 sec + last = 513
  currentSong[3] = new SoundFile(this, "Secret Operation.wav"); //2:59 - 179 sec + last = 693
  currentSong[4] = new SoundFile(this, "Metaphysics.wav");      //3:43 - 223 sec + last = 917
  currentSong[5] = new SoundFile(this, "Martini.wav");          //4:48 - 288 sec + last = 1206
  println("audio loaded.");
  println("loading movies");
  green = new Movie(this, "green.mp4");
  cobred = new Movie(this, "red.mp4");
  intro = new Movie(this, "laser_maze.mp4");
  bestUnicorn = new Movie(this, "bestUnicorn.mp4");
  println("movies loaded.");
  cobra = loadImage("logo.jpg");
  mars = loadImage("mars.png");
  destro = loadImage("pdDestro.jpg");
  connect(1, 24, 200); // 25 com ports*4 port numbers *2 tries each
  myPort.write('L'); // make sure Arduino knows Processing just launched/relaunched
  gameStage = 0;
}

void draw() {
  background(0); // necessary so everything doesn't draw on top of itself over and over
  switch (gameStage) {
  case 0:
    gameReset();
    gameStage = 10;
    break;
  case 10:
    checkMusic();
    setMediaDuringGameplay();
    gameStage = 20;
    break;
  case 20:
    gameStage = 100;
    gameTimer.reset();
    break;
  case 100:
    background(0);
    drawGreen();
    displaySplashScreen(); // instruction to press start and high score display
    checkMusic();
    if (isDestro == false)drawMars();
    else if (isDestro == true) drawDestro();
    break;
    // play the Gman intro Movie
  case 200:
    background(0);
    drawGreen();
    textAlign(CENTER);
    textFont(font, 48);
    fill(0, 80, 0);
    text("press START to skip", width/2, height*0.95);
    currentSong[trackCount].amp(0.2);
    if ((intro.time() >= 15.0)&&(intro.time() <= 22.0)) {
      currentSong[trackCount].amp(0.0);
    }
    if (!intro.isPlaying()) {
      intro.play();
    } 
   if (intro.time() == intro.duration()) {
      intro.stop();
      background(0);
      gameStage = 250;
    } 
      drawGman();
    break;
  case 250:
    background(0);
    setMediaDuringGameplay();
    currentSong[trackCount].amp(0.4);
    countdown.start();
    if (readyArduino == false) {
      myPort.write('P');
      readyArduino = true;
    }
    gameStage = 300;
    break;
    // 3, 2, 1, GO
  case 300:
    background(0);
    drawGreen();
    showCountdown();
    // start the actual gameplay
    if (countdown.second() == 5) {
      gameStage = 350;
    }
    break;
  case 350:
    background(0);
    countdown.reset();
    if (readyArduino == false) {
      myPort.write('P'); // let arduino know it's ok to register alarms
      readyArduino = true;
    }
    gameTimer.start();
    gameStage = 390;
    break;
  case 390: // called at end of "regtime" serial event to make sure music retures after unicorn spell
    background(0);
    setMediaDuringGameplay(); // reset music before we leave
    gameStage = 395;
    break;
  case 395:
    gameStage = 400;
    break;
  case 400:
    background(0);
    checkMusic();
    currentSong[trackCount].amp(0.8);
    drawGreen();
    showRunningGame();
    if (isDestro == false) {
      drawMarsButton();
    } else if (isDestro == true) {
      drawDestroButton();
    }
    // checkASCII();
    // unicorn magic time
    break;
  case 500:
    background(0);
    drawGreen();
    checkMusic();
    if (!bestUnicorn.isPlaying()) {
      bestUnicorn.loop();
    }
    bubblesTransition();
    if (isSlowed == false) {
      slowMusic();
      isSlowed = true;
    }
    drawUnicorn();
    alreadyUsedSpell = true; // set flag so one spell per game only
    break;
  case 670:
    background(0);
    setAlarm();
    gameStage = 680;
    break;
  case 680:
    background(0);
    showAlarm();
    break;
  case 690:
    background(0);
    if (readyArduino == true) {
      myPort.write('p'); // let arduino know it's ok to register alarms
      readyArduino = false;
      gameStage = 700;
    }
    break;
  case 700:// end of game - show final score
    background(0);
    checkMusic();
    if (score <= 3) {
      drawGreen();
      showFinalScoreG();
      if (isDestro == false)drawMars();
      else if (isDestro == true) drawDestro();
    } else if (score >= 4) {
      cobra();
      showFinalScoreR();
      drawCobra();
    }
    break;
  case 800:
    background(0);
    cobra();
    break;
    // end of switch gameStage
  }
}

// gameStages
// 0 exits to 10
// resets game variables to defaults for new game
void gameReset() {
  score = 0;
  i = 3;
  currentTimeVal = 1000.0;
  introStarted = false;
  alreadyUsedSpell = false;
  isSlowed = false; // set flag so this only happens once per game
  isDestro = false;
  textFont(font, 120);
  fill(0, 200, 0);
  if (intro.isPlaying()) {
    intro.stop();
  }
  if (cobred.isPlaying()) {
    cobred.stop();
  }
  if (green.isPlaying()) {
    green.stop();
  }

  if (alarm.isPlaying()) {
    if (alarm.position() == alarm.duration()) {
      alarm.jumpFrame(0);
    }
    alarm.stop();
  }
  if (Hydra.isPlaying()) {
    //Hydra.pause();
    if (Hydra.position() == Hydra.duration()) {
      Hydra.jumpFrame(0);
    }
    Hydra.stop();
  }
  if (currentSong[trackCount].isPlaying()) {
    currentSong[trackCount].stop();
  }
  currentSong[trackCount].removeFromCache();
}

void displaySplashScreen() {
  textSize(60);
  textAlign(CENTER);
  fill(#EDE902);
  text("PRESS START", width/2, height/6);
  textAlign(LEFT);
  text("High Score", (width/4), height * 0.85);
  if (todaysHighScore >= yourTopScore) {
    textAlign(RIGHT);
    text(todaysHighScore, (width/4)*3.30, height* 0.85);
  } else {
    text(yourTopScore, (width/4) * 3.30, height* 0.85);
    todaysHighScore = yourTopScore;
  }
}
void setMediaDuringGameplay() {
  if (intro.isPlaying()) {
    intro.stop();
  }
  if (Hydra.isPlaying()) {
    Hydra.pause();
  }
  if (alarm.isPlaying()) {
    alarm.pause();
  }

  tint(255);
  if (!green.isPlaying()) {
    green.play();
    image(green, width/2, height/2);
  }
  if (green.time() > 28.0) {
    green.jump(0);
  }
}
void showCountdown() {
  textSize(280);
  textAlign(CENTER);
  fill(#EDE902); // gold
  if (countdown.second() == 1) {
    text("3", width/2, height/1.7);
  } else if (countdown.second() == 2) {
    text("2", width/2, height/1.7);
  } else if (countdown.second() == 3) {
    text("1", width/2, height/1.7);
  } else if (countdown.second() == 4) {
    text("GO!", width/2, height/1.7);
  }
}

void showRunningGame() {
  textAlign(CENTER);
  textFont(font, 140); // default 120
  fill(#EDE902); // gold
  text("time: ", (width/5.0), height/2 - 80); // 3.2
  text (nf(gameTimer.minute(), 2) + ":" + nf(gameTimer.second(), 2), width/1.5-150, height/2 -80);
  text (nf(gameTimer.millis()/10, 3), width - 280, height/2 -80);
  text("alarms: ", (width/3.5), height /2 + 140);
  text(score, (width/1.5), height/2 + 140);
}
void bubblesTransition() {
  strokeWeight(10);
  fill(255, 10);
  noStroke();
  rect(width/2, height/2, width, height);
  float radius = random(0, 100);
  float red = random(0, 255);
  float green = random(0, 255);
  float blue = random(0, 255);
  float alpha = random(0, 255);
  stroke(red, green, blue, alpha);
  fill(255-red, 255-green, 255-blue, alpha);
  ellipse(random(0, height), random(0, width), radius * 2, radius * 2);
  textAlign(CENTER);
  textFont(font, 80);
  fill (0, 200, 0);
  text("time", (width/4), height/2 - 400);
  text(str(gameTimer.time()/100), (width/4 + 900), height/2 -400);
  text("alarms", (width/4 -100), height/2 +400);
  text(score, (width/4 + 900), height/2 + 400);
}
// 670 exits to 680
void setAlarm() {
  if (intro.isPlaying()) {
    intro.stop();
  }
  if (Hydra.isPlaying()) {
    Hydra.pause();
  }
  if (!currentSong[trackCount].isPlaying()) {
    currentSong[trackCount].play();
    currentSong[trackCount].amp(0.7);
  }
  if (alarm.isPlaying()) {
    if (alarm.position() == alarm.duration()) {
      alarm.jumpFrame(0);
    }
  } else if (!alarm.isPlaying()) {
    alarm.play();
    alarm.amp(1);
  }
}
// 680 exits back to 400
void showAlarm() {
  textAlign(CENTER);
  fill(212, 175, 55);
  textFont(font, 166);
  text("!! INTRUDER !!", width/2, height/1.98);
  fill(0, 0, 0);
  textFont(font, 164);
  text("!! INTRUDER !!", width/2, height/2);
  fill(255, 255, 255);
  textFont(font, 164 );
  text("!! INTRUDER !!", width/2, height/2.02);
  strokeCap(SQUARE);
  strokeWeight(random(width/100, (width/100)*2));
  float red = random(0, 50);
  if (red < 1 || currentSong[trackCount].isPlaying()) red = random(0, 255);
  for (int k = 0; k < 4; k++) {
    stroke(red, 0, 0);
    float x = random(0, width);
    line(x, 0, x, height);
  }
  float csize = random(150, 200);
  fill(#D4AF37);
  circle(width*0.9, height/6, csize);
  fill(#D4AF37);
  circle(width*0.9, height*0.8, csize);
  fill(#D4AF37);
  circle(width/9, height/6, csize);
  fill(#D4AF37);
  circle(width/9, height*0.8, csize);
}
void showFinalScoreG() {
  currentSong[trackCount].amp(0.5);
  textSize(60);
  textAlign(CENTER);
  fill(#EDE902); // gold
  text("time: ", (width/3.2), height/2 - 360);
  text(str(gameTimer.time()/100), (width/1.5), height/2 -360);
  text("alarms: ", (width/3.2), height/2 - 260);
  text(score, (width/1.5), height/2 - 260);
  yourScore = score + 1;
  yourFactor = gameTimer.time()/100 * yourScore;
  yourScore = 1000000 - yourFactor;
  text("Your Score: ", (width/3.2), height/2 + 330);
  text(yourScore, (width/1.5), height/2 + 330);
  yourTopScore = yourScore;
  text("High Score", (width/3.2), height/2 +430);
  if (todaysHighScore >= yourTopScore) {
    textAlign(RIGHT);
    text(todaysHighScore, (width/1.5), height/2 + 430);
  } else {
    text(yourTopScore, (width/4) * 3.30, height* 0.85);
    todaysHighScore = yourTopScore;
  }
  textAlign(CENTER);
  textFont(font, 48);
  fill(0, 100, 0);
  text("press RESET button", width/2, height*0.98);
}
void showFinalScoreR() {
  currentSong[trackCount].amp(0.5);
  textSize(60);
  textAlign(CENTER);
  fill(#EDE902); // gold
  text("time: ", (width/3.2), height/2 - 360);
  text(str(gameTimer.time()/100), (width/1.5), height/2 -360);
  text("alarms: ", (width/3.2), height/2 - 260);
  text(score, (width/1.5), height/2 - 260);
  yourScore = score + 1;
  yourFactor = gameTimer.time()/100 * yourScore;
  yourScore = 1000000 - yourFactor;
  text("Your Score: ", (width/3.2), height/2 + 330);
  text(yourScore, (width/1.5), height/2 + 330);
  yourTopScore = yourScore;
  text("High Score", (width/3.2), height/2 +430);
  if (todaysHighScore >= yourTopScore) {
    textAlign(RIGHT);
    text(todaysHighScore, (width/1.5), height/2 + 430);
  } else {
    text(yourTopScore, (width/4) * 3.30, height* 0.85);
    todaysHighScore = yourTopScore;
  }
  textAlign(CENTER);
  textFont(font, 48);
  fill(#EDE902);
  text("press RESET button", width/2, height*0.98);
}

// graphics 
void drawGreen() {
  tint(255);
  if (!green.isPlaying()) {
    green.play();
  }
    if (mousePressed) {
    clip(mouseX, mouseY, 400, 400);
  } else {
    noClip();
  }
  image(green, width/2, height/2);
  if (green.time() > 28.0) {
    green.jump(0);
  }
}
void cobra() {
  if (currentSong[trackCount].isPlaying()) {
    currentSong[trackCount].pause();
  }
  if (!Hydra.isPlaying()) {
    Hydra.loop();
    Hydra.amp(0.7);
  }
  if (!cobred.isPlaying()) {
    cobred.play();
  }
  image(cobred, width/2, height/2);
  if (cobred.time() > 28.0) {
    cobred.jump(0);
  }
}
void drawMars() {
  textureMode(NORMAL);
  noStroke();
  directionalLight(5, 255, 28, width/5, height*5, 195);
  pointLight(255, 255, 255, width/4, height*4, 200);
  pointLight(255, 255, 255, width*4, height/4, 200);
  pointLight(255, 255, 255, width/4, height/4, 205);
  pointLight(255, 255, 255, width*4, height*4, 205);
  ambientLight(255, 255, 255);
  translate(width/2.0, height/2.0, 200); // 1080p
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(140);
  tint(255, 166);
  MarsCube(mars);
}
void drawCobra() {
  noStroke();
  translate(width/2.0, height/2.0, 200); // -1000?
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(140);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, height/2.1, 0);
  pointLight(129, 129, 129, width/1.9, height*1.5, 0);
  pointLight(129, 129, 129, width/2.1, height/2.0, 0);
  //  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  emissive(129, 0, 0);
  tint(255, 176);
  // specular(255, 255, 255);
  CobraCube(cobra);
}
void drawDestro() {
  noStroke();
  translate(width/2.0, height/2.0, 200); // -1000?
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(140);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, height/2.1, 0);
  pointLight(129, 129, 129, width/1.9, height*1.5, 0);
  pointLight(129, 129, 129, width/2.1, height/2.0, 0);
  //  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  emissive(129, 0, 0);
  tint(255, 176);
  // specular(255, 255, 255);
  DestroCube(destro);
}
void drawMarsButton() {
  textureMode(NORMAL);
  noStroke();
  translate(width/2, 820, 80);
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(80);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, 910, 0);
  pointLight(129, 129, 129, width/1.9, 890, 0);
  pointLight(129, 129, 129, width/2.1, 930, 0);
  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  tint(255, 166);
  MarsCube(mars);
}
void drawCobraButton() {
  textureMode(NORMAL);
  noStroke();
  translate(width/2, 800, 80);
  translate(width/2, 820, 80);
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(80);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, 910, 0);
  pointLight(129, 129, 129, width/1.9, 890, 0);
  pointLight(129, 129, 129, width/2.1, 930, 0);
  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  emissive(129, 0, 0);
  tint(255, 176);
  specular(255, 255, 255);
  CobraCube(cobra);
}
void drawDestroButton() {
  textureMode(NORMAL);
  noStroke();
  translate(width/2, 820, 80);
  rotx += frameRate*0.00008;
  roty += frameRate*0.00008;
  rotateX(rotx);
  rotateY(roty);
  scale(80);
  lightSpecular(255, 255, 255);
  pointLight(129, 129, 129, width/2.1, 910, 0);
  pointLight(129, 129, 129, width/1.9, 890, 0);
  pointLight(129, 129, 129, width/2.1, 930, 0);
  directionalLight(255, 255, 255, mouseX, mouseY, -100);
  tint(255, 166);
  DestroCube(destro);
}
void drawGman() {
  push();
  textureMode(NORMAL);
  noStroke();
  lightSpecular(255, 255, 255);
  pointLight(255, 255, 255, width/4, height*4, 80); // 200
  pointLight(255, 255, 255, width*4, height/4, 80); // 200
  pointLight(255, 255, 255, width/4, height/4, 85); // 205
  pointLight(255, 255, 255, width*4, height*4, 85); // 205
  translate(width/2.0, height/2.0, 250); // 1080p
  roty += frameRate*0.00008;
  rotateY(roty);
  scale(220); 
  tint(255, 205); // 186
  GCube(intro);
  pop();
}
void drawUnicorn() {
  textureMode(NORMAL);
  noStroke();
  lightSpecular(255, 255, 255);
  pointLight(255, 255, 255, width/4, height*4, 200);
  pointLight(255, 255, 255, width*4, height/4, 200);
  pointLight(255, 255, 255, width/4, height/4, 205);
  pointLight(255, 255, 255, width*4, height*4, 205);
  translate(width/2.0, height/2.0, 200); // 1080p
  roty += frameRate*0.00008;
  rotateY(roty);
  scale(220); // DEFAULT 180
  tint(255, 166);
  uniCube(bestUnicorn);
}
void MarsCube(PImage mars) {
  beginShape(QUADS);
  texture(mars);
  getVertices();
  endShape();
}
void CobraCube(PImage cobra) {
  beginShape(QUADS);
  texture(cobra);
  getVertices();
  endShape();
}
void DestroCube(PImage destro) {
  beginShape(QUADS);
  texture(destro);
  getVertices();
  endShape();
}
void GCube(Movie intro) {
  beginShape(QUADS);
  texture(intro);
  getVertices();
  endShape();
}
void uniCube(Movie bestUnicorn) {
  beginShape(QUADS);
  texture(bestUnicorn);
  getVertices();
  endShape();
}
void movieEvent(Movie m) {
  if (m == intro) {
    intro.read();
  } else if (m == green) {
    green.read();
  } else if (m == cobred) {
    cobred.read();
  } else if (m == bestUnicorn) {
    bestUnicorn.read();
  }
}

// utilities
void printSerialMenu() {
  println("========== laserMaze2025_v1_0 =========");
  println("== comp: laserMaze2025DualMode_v1.0 ===");
  println("========== MENU =========");
  println("d/D ZERO alarmSystemArmed");
  println("a/A SET alarmSystemArmed");
  println("b/B ON NETWORK toggle hardMode");
}
   void keyPressed(){
    if (key == '?') {
      println("gameStage: " + gameStage);
      println("isDestro: " + isDestro);
      println("readyArduino: " + readyArduino);
      println("isSlowed: " + isSlowed);
      println("alreadyUsedSpell: " + alreadyUsedSpell);
      println("timesReset: " + timesReset);
    } else if (key == 'm') {
      printSerialMenu();
    }
    delay(160);
  }

void checkMusic() {
  if (!currentSong[trackCount].isPlaying()) {
    trackCount = (trackCount + 1) % currentSong.length;
    currentSong[trackCount].play();
    gameCounter = trackCount;
    currentSong[trackCount].amp(0.7);
  }
}
void slowMusic() {
  //slow the music down
  currentSong[trackCount].rate(1.0);
  float thisRate = audioTopRate;
  for (float i = 0.0; i <= 80.0; i++) {
    thisRate -= 0.01;
    currentSong[trackCount].rate(thisRate);
    delay(50);
    // println(thisRate);
  }
  currentSong[trackCount].rate(0.1);
}
void speedMusic() {
  currentSong[trackCount].jump(3.5);
  currentSong[trackCount].rate(0.1);
  // speed the music back up
  for (float i = 0.1; i <= 9; i++) {
    currentSong[trackCount].rate(audioBottomRate + i);
    delay(150);
  }
  currentSong[trackCount].rate(1.0);
}
// returns your float truncated to tenths position for time calc
float truncate(float x) {
  if ( x > 0 )
    return float(floor(x * 10))/10;
  else
    return float(ceil(x * 10))/10;
}
void getVertices() {
  // +Z "front" face
  vertex(-1, -1, 1, 0, 0);
  vertex( 1, -1, 1, 1, 0);
  vertex( 1, 1, 1, 1, 1);
  vertex(-1, 1, 1, 0, 1);
  // -Z "back" face
  vertex( 1, -1, -1, 0, 0);
  vertex(-1, -1, -1, 1, 0);
  vertex(-1, 1, -1, 1, 1);
  vertex( 1, 1, -1, 0, 1);
  // +Y "bottom" face
  vertex(-1, 1, 1, 0, 0);
  vertex( 1, 1, 1, 1, 0);
  vertex( 1, 1, -1, 1, 1);
  vertex(-1, 1, -1, 0, 1);
  // -Y "top" face
  vertex(-1, -1, -1, 0, 0);
  vertex( 1, -1, -1, 1, 0);
  vertex( 1, -1, 1, 1, 1);
  vertex(-1, -1, 1, 0, 1);
  // +X "right" face
  vertex( 1, -1, 1, 0, 0);
  vertex( 1, -1, -1, 1, 0);
  vertex( 1, 1, -1, 1, 1);
  vertex( 1, 1, 1, 0, 1);
  // -X "left" face
  vertex(-1, -1, -1, 0, 0);
  vertex(-1, -1, 1, 1, 0);
  vertex(-1, 1, 1, 1, 1);
  vertex(-1, 1, -1, 0, 1);
}

// serial handler
void serialEvent (Serial myPort) {
  String inString = myPort.readStringUntil('\n');
  if (inString != null) {
    inString = trim(inString);
    if (gameStage == 400) {
      if (inString.equals("alarm")) {
        println("alarm");
        score+=1;
        gameStage = 670;
      }
    }
    if ((gameStage == 670)||(gameStage == 680)) {
      if (inString.equals("off")) {
        println("off");
        gameStage = 400; // return to reg play
      }
    }
    if (inString.equals("start")) {
      println("start");
      if (gameStage == 100) {
        gameStage = 200;
      } else if (gameStage == 200) { // skips video midway if desired
        if (intro.isPlaying()) {
          intro.stop();
        }
        gameStage = 250;
      }
    }
    if (inString.equals("stop")) {
      if (gameStage == 400 || gameStage == 500 || gameStage == 670 || gameStage == 680 || gameStage == 690) {
        if (intro.isPlaying()) {
          intro.stop();
        }
        if (alarm.isPlaying()) {
          if (alarm.position() == alarm.duration()) {
            alarm.jumpFrame(0);
            alarm.stop();
          }
        }
        //  if (gameStage == 400 || gameStage == 500 || gameStage == 670 || gameStage == 680) {
        println("stop");
        gameTimer.pause();
        gameCounter++;
        gameStage = 690;
      }
    }
    if (inString.equals("reset")) {
      if (gameStage == 400 || gameStage == 500 || gameStage == 670 || gameStage == 680 || gameStage == 690 || gameStage == 700) {
      if (intro.isPlaying()) {
        intro.stop();
      }
      if ((gameStage != 0)||(gameStage != 10)) {
        println("reset");
        if (readyArduino == true|| readyArduino == false) {
          myPort.write('p');
          readyArduino = false;
        }
        timesReset ++;
        if (!currentSong[trackCount].isPlaying()) {
          currentSong[trackCount].stop();
        }
        gameReset();
        gameStage = 10;
      }
      }
    }
    if (inString.equals("timewarp")) {
      if (alreadyUsedSpell == false) { // toggle in gameStage state machine
        if (gameStage == 400) {
          println("timewarp");
          gameTimer.pause();
          currentSong[trackCount].amp(1.0);
          gameStage = 500;
        }
      }
    }
    if (inString.equals("regtime")) {
      println("regtime");
      gameTimer.restart();
      currentTimeVal = 1000.0;
      speedMusic();
      gameStage = 390; // checks and resets music if needed
    }
    // check for remote Destro cube swap trigger
    if (inString.equals("destro")) {
      println("destro");
      isDestro = true;
    }
    if (inString.equals("mars")) {
      println("mars");
      isDestro = false;
    }
  }
}

// connect
boolean connect(int portNumber, int numCom, int limit) {
  background (0, 14, 255);
  int tries = 0;
  while (tries < limit) {
    try {
      printArray (Serial.list()[portNumber]);
      myPort = new Serial(this, com[numCom], 115200);
    }
    catch(Exception e) {
      System.err.println("Retrying " + "Port " + com[numCom] + " on Port Bus " + portNumber);
      // numCom -= 1;
      numCom += 1;
      tries++;
      if (numCom < 0) { // prevent ArrayIndexOutOfBoundsException
        numCom = 24;
      } else if (numCom > 24) { // prevent ArrayIndexOutOfBoundsException
        numCom = 0;
      }
      if (tries % 25 == 0) {
        portNumber += 1;
        if (portNumber > 4) {
          portNumber = 0;
        }
      }
      delay(200);
      continue;
    }
    break;
  }
  if (tries < limit) {
    println("Connected in " + str(tries + 1) + " tries.");
    background(0, 255, 0);
    delay(200);
    background(0);
    return true;
  } else {
    System.err.println("Connection failed.");
    return false;
  }
}

Happy Hallowe’en!

edit: I tried to share all the code but it wouldn’t let me so the Arduino side isn’t here

1 Like

Please to see that you got it sorted out!

Happy Hallowe’en!

:)

1 Like