How can I switch between ControlP5s without skipping pages?

I’m creating a game in processing and I’m having issues with my GUI using ControlP5. My current setup is that there is a startwindow ControlP5, a tankswindow ControlP5, a howtoplaywindow ControlP5, etc. My issue is that when I click certain buttons the window is supposed to switch, but the sketch is skipping those windows and going directly to another one. This occurs specifically when, in the tankswindow, you select “Restart.” This is supposed to take you back the startwindow, which has an option to create another tankswindow, however the sketch goes directly to a new tankswindow. This also occurs when someone wins the game. After this occurs, a winnerwindow is supposed to be displayed, which has an option to “Play Again” or “Exit.” If the user selects “Play Again,” the sketch should go back to the startwindow, which has an option to go back to the tankswindow. Again, the sketch skips both the winnerwindow (only sometimes though) and the startwindow, going directly to the tankswindow.

The game is Tanks, and the rules are just to click and drag your mouse to launch projectiles at your enemy tank. Here is my code:

TanksWindow (where the sketch is drawn):

import processing.sound.*;
import controlP5.*;


ControlP5 startwindow, tankswindow, howtoplaywindow, htptankswindow, winnerwindow; //Different windows with different buttons, labels, and textareas
Tank myTank, yourTank; 
Wall[] walls;
Projectile p;
SoundFile shoot, explosion, tankExplosion;
boolean turn; // Determines which tank's turn it is; true is left, false is right
Button start, howtoplay, htpstart, restart, htptanks, resume, playagain, exit; 
Textlabel tankslabel, TWtankslabel, howtoplaylabel, congratslabel, htptankslabel, menulabel, twhowtoplaylabel;
Textarea instructions, twinstructions;
PImage heart; // For lives
float wind; //X accelleration representing wind
boolean paused; //determines if functionality is paused after a button press (to avoid bad actions)
int pauseframe;//determines when first paused
String pausebefore;

void setup(){
   size(800, 400);

   shoot = new SoundFile(this, "tanksound.mp3"); // played when tank shoots
   explosion = new SoundFile(this, "explodesound.mp3"); // played when projectile explodes
   tankExplosion = new SoundFile(this, "tankexplodesound.mp3"); //Plays when tank explodes
   heart =  loadImage("heart.png"); //For lives
   p = new Projectile(-10,-10, 0, 0); //initializes projectile off screen
  paused = false;

  createWindows(); // creates each window and makes them invisible to start
  setupStartWindow();
}

void createWindows(){
  startwindow = new ControlP5(this);
  tankslabel = startwindow.addTextlabel("TANKSlabel")
   .setText("TANKS")
   .setPosition(312.5,50)
   .setSize(175,50)
   .setFont(createFont("armalite.ttf",50))
   .setColor(0);
   ;
  start = startwindow.addButton("Start")
  .setLabel("Start")  
  .setSize(100,50)
  .setPosition(width/2 - 50, 150)
  .setColor(new CColor(0, 0xff005000, 0, 0xff999999, 0));
   ;
  howtoplay = startwindow.addButton("How To Play")
  .setLabel("How To Play")
  .setSize(100,50)
  .setPosition(width/2 - 50, 225)
  .setColor(new CColor(0, 0xff005000, 0, 0xff999999, 0));
  ;
  startwindow.setVisible(false);

  tankswindow = new ControlP5(this);
  TWtankslabel = tankswindow.addTextlabel("twTANKSlabel")
   .setText("TANKS")
   .setPosition(25,25)
   .setSize(175,50)
   .setFont(createFont("armalite.ttf",25))
   .setColor(255);
   ;
   menulabel = tankswindow.addTextlabel("MenuLabel")
   .setText("MENU")
   .setPosition(650,50)
   .setSize(100,50)
   .setFont(createFont("armalite.ttf",35))
   .setColor(255);
   ;
 htptanks = tankswindow.addButton("How to Play TANKS")
  .setLabel("How To Play")
  .setSize(100,50)
  .setPosition(650, 125)
  .setColor(new CColor(0, 0, 0, 0xff999999, 0));
  ;
  restart = tankswindow.addButton("Restart")
   .setLabel("Restart")
   .setSize(100,50)
   .setColor(new CColor(0, 0, 0, 0xff999999, 0))
   .setPosition(650, 200);
  ;
  tankswindow.setVisible(false);

  howtoplaywindow = new ControlP5(this); 
   instructions = howtoplaywindow.addTextarea("instructions")
  .setPosition(width/2 - 125, 125)
  .setSize(300, height - 150)
  .setColor(0)
  .setFont(createFont("arial",18))
  .setText("The aim of Tanks is to destroy your enemy tank before they destroy you. To destroy your enemy tank, you must hit them 5 times with a projectile. To shoot, use your mouse to click on the screen. Holding the mouse will show an arrow, which represents the initial speed and angle of the projectile. Releasing the mouse shoots the projectile based on the arrow.");
  ;
  htptankslabel = howtoplaywindow.addTextlabel("htpTANKSlabel")
 .setText("TANKS")
 .setPosition(25,25)
 .setSize(175,50)
 .setFont(createFont("armalite.ttf",25))
 .setColor(0);
 ;
  howtoplaylabel = howtoplaywindow.addLabel("howtoplay")
  .setPosition(width/2 - 112, 75)
  .setSize(225,50)
  .setColor(0)
  .setText("How To Play")
  .setFont(createFont("armalite.ttf",35));
  ;
  htpstart = howtoplaywindow.addButton("HTPStart")
  .setLabel("Start")
  .setSize(100,50)
  .setPosition(width/2 - 50, height - 62.5)
  .setColor(new CColor(0, 0xff005000, 0, 0xff999999, 0));
  ;
  howtoplaywindow.setVisible(false);

  htptankswindow = new ControlP5(this);

  twinstructions = htptankswindow.addTextarea("twinstructions")
  .setPosition(125, 150)
  .setSize(350, 150)
  .setColor(255)
  .setFont(createFont("arial",18))
  .setText("The aim of Tanks is to destroy your enemy tank before they destroy you. To destroy your enemy tank, you must hit them 5 times with a projectile. To shoot, use your mouse to click on the screen. Holding the mouse will show an arrow, which represents the initial speed and angle of the projectile. Releasing the mouse shoots the projectile based on the arrow.");
  ;
  twhowtoplaylabel = htptankswindow.addTextlabel("twhowtoplaylabel")
 .setText("How to Play")
 .setPosition(200, 100)
 .setSize(177,50)
 .setFont(createFont("armalite.ttf",25))
 .setColor(255);
 ;
  resume = htptankswindow.addButton("Resume")
  .setLabel("Resume")
  .setSize(100,50)
  .setPosition(width/2 - 50, 325)
  .setColor(new CColor(0, 0xff005000, 0, 0xff999999, 0));
  ;
  htptankswindow.setVisible(false);

  winnerwindow = new ControlP5(this);
  congratslabel = winnerwindow.addTextlabel("congratslabel")
 .setText("Congrats")
 .setPosition(625,50)
 .setSize(175,50)
 .setFont(createFont("armalite.ttf",15))
 .setColor(255);
 ;

  playagain = winnerwindow.addButton("Play Again")
  .setLabel("Play Again")
  .setPosition(650, 100)
  .setSize(100,50)
  .setColor(new CColor(0, 0, 0, 0xff999999, 0));
  ;
  exit = winnerwindow.addButton("Exit")
  .setLabel("Exit")
  .setPosition(650, 175)
  .setSize(100,50)
  .setColor(new CColor(0, 0, 0, 0xff999999, 0));
  ;
  winnerwindow.setVisible(false);
}
void setupStartWindow(){
  background(135,206,250);//Sky Blue
  startwindow.setVisible(true);
}

void setupTanksWindow(){
  background(135,206,250); //Sky Blue
  //Randomizes height & width of walls & location of tanks each time a new game is created
  walls = new Wall[3];
  walls[0] = new Wall(random(225,275), random(50,150), random(325, 375) );
  walls[1] = new Wall(0, random(200,350), walls[0].leftX);
  walls[2] = new Wall(walls[0].rightX, random(200,350), width-200);
  yourTank = new Tank( walls[0].rightX + random(50,150), walls[2].leftY - 30, false, "Player 2");
  myTank = new Tank(walls[0].leftX - random(100,200), walls[1].leftY - 30, true, "Player 1");

  //Menu bar
  fill(0,100,0);
  stroke(0,100,0);
  rect(600,0,200,400);


  turn = true;//Starts as player 1s turn
  tankswindow.setVisible(true);

  //Adds lives to screen
  for(int i = 0; i < 5-myTank.numHits; i++){
    image(heart, myTank.xcoor + i*20, 75, 15, 15);
  }
  for(int i = 0; i < 5-yourTank.numHits; i++){
    image(heart, yourTank.xcoor + i*20, 75, 15, 15);
  }

  //Arrow representing the amount of wind
  stroke(256,256, 256);
  fill(256,256, 256);
  line(300, 25, 300+ 200* wind, 25);
  if(wind>0){
      triangle(300+ 200* wind, 25, 300+ 200* wind - 7, 20, 300+ 200* wind - 7, 30);
  }else{
    triangle(300+ 200* wind, 25, 300+ 200* wind + 7, 20, 300+ 200* wind + 7, 30);
  }

}

void setupHowToPlayWindow(){
  background(135,206,250);
  howtoplaywindow.setVisible(true);
}
void setupHTPTanks(){
  fill(0);
  stroke(0);
  rect(100,75,400, 300);
  htptankswindow.setVisible(true);
}

void setupWinnerWindow(String name){
  fill(0,100,0);
   stroke(0,100,0);
   rect(600,0,200,400);
  congratslabel.setText("Congrats " + name);
  winnerwindow.setVisible(true);
}

//redraws tankswindow so the projectile appears to be moving
void rescreen(){
   background(135,206,250);
   for(int i = 0; i < walls.length;i++){
     walls[i].redraw();
   }
   fill(0,100,0);
   stroke(0,100,0);
   rect(600,0,200,400);
   myTank.redraw();
   yourTank.redraw();

   //Draws lives
   for(int i = 0; i < 5-myTank.numHits; i++){
    image(heart, myTank.xcoor + i*20, 75, 15, 15);
  }
  for(int i = 0; i < 5-yourTank.numHits; i++){
    image(heart, yourTank.xcoor + i*20, 75, 15, 15);
  }

  //Represents wind
  stroke(256,256, 256);
  fill(256,256, 256);
  line(300, 25, 300+ 200* wind, 25);
  if(wind>0){
      triangle(300+ 200* wind, 25, 300+ 200* wind - 7, 20, 300+ 200* wind - 7, 30);
  }else{
    triangle(300+ 200* wind, 25, 300+ 200* wind + 7, 20, 300+ 200* wind + 7, 30);
  }
}

void draw(){
  //Tried to "pause" the sketch to stop any actions from accidentally being triggered after I pressed a certain button
  if(paused){
    if(pauseframe + 10 >= frameCount){
      paused = false;
    }else{
      return;
    }
  }
  if(tankswindow.isVisible()){
    if(myTank.numHits >= 5 && !myTank.exploding){
      setupWinnerWindow(myTank.name);
      tankswindow.setVisible(false);
      paused = true;
      pauseframe = frameCount;
      return;
  }
  if(yourTank.numHits >= 5 && !yourTank.exploding){
      setupWinnerWindow(yourTank.name);
      tankswindow.setVisible(false);
      paused = true;
      pauseframe = frameCount;
      return;
  }
    if(p!= null && p.exists){
      rescreen();
      p.move();
    }else if(p != null && !p.exists && p.exploding){
        p.explode();
    }
    if(myTank.exploding){
      myTank.explode();
    }
      if(yourTank.exploding){
      yourTank.explode();
    }
    if(restart.isPressed()){
      tankswindow.setVisible(false);
      setupStartWindow();
      paused = true;
      pauseframe = frameCount;
      return;
    }
    if(htptanks.isPressed()){
      tankswindow.setVisible(false);
      setupHTPTanks(); 
      paused = true;
      pauseframe = frameCount;
      return;
    }
  }
  if(htptankswindow.isVisible()){
    if(resume.isPressed()){
      rescreen();
      htptankswindow.setVisible(false);
      tankswindow.setVisible(true);
       paused = true;
      pauseframe = frameCount;
      return;
    }
  }
  if(startwindow.isVisible()){
    if (start.isPressed()){
      startwindow.setVisible(false);
       setupTanksWindow();
        paused = true;
      pauseframe = frameCount;
      return;
    }
    if(howtoplay.isPressed()){
       startwindow.setVisible(false);
      setupHowToPlayWindow();
       paused = true;
      pauseframe = frameCount;
      return;
    }
  }
  if(howtoplaywindow.isVisible()){
    if(htpstart.isPressed()){
      howtoplaywindow.setVisible(false);
      setupTanksWindow();
       paused = true;
      pauseframe = frameCount;
      return;
    }
  }
  if(winnerwindow.isVisible()){
    if(playagain.isPressed()){
      winnerwindow.setVisible(false);
      setupStartWindow();
       paused = true;
      pauseframe = frameCount;
      return;
      //pausebefore = "SW";
    }
    if(exit.isPressed()){
      exit();
    }
  }
}

//Creates arrow representing initial velocity and angle
float startX, startY, endX, endY;
void mousePressed(){
  if(!paused && tankswindow.isVisible() && mouseX < 600 && !p.exists){
  endX = mouseX;
  endY = mouseY;
  if(turn){
  startX = myTank.xcoor+33;
  startY = myTank.ycoor + 7;
  }else{
    startX = yourTank.xcoor+25;
  startY = yourTank.ycoor + 7;
  }
  makeline();
  }
}


void mouseDragged(){
  if(!paused && tankswindow.isVisible() && mouseX < 600 && !p.exists){
 endX = mouseX;
 endY = mouseY;
 rescreen();
 makeline();
  }
}

//Shoots the projectile
void mouseReleased(){
  if(!paused && tankswindow.isVisible() && mouseX < 600 && !p.exists){
  if(turn){
    p = myTank.shoot(dist(startX, startY, endX, endY)/ 10, atan2(endY-startY, endX-startX));
  }else{
    p = yourTank.shoot(dist(startX, startY, endX, endY)/ 10, atan2(endY-startY, endX-startX));
  }
  turn = !turn;
  }
}

//creates line to represent initial velocity & angle
void makeline(){ 
  if(!paused && tankswindow.isVisible()){
    //Makes arrow representing initial velocity
    stroke(255,69,0);
    fill(255,69,0);
    pushMatrix();
    translate(endX, endY);
    rotate(atan2(endY-startY, endX-startX));
    triangle(-10,-7, -10,7, 0,0);
    popMatrix();
    line(startX, startY, endX, endY);
    pushMatrix();
    if(turn){
      translate(myTank.xcoor+33, myTank.ycoor + 7);
    }else{
      translate(yourTank.xcoor+25, yourTank.ycoor + 7);
    }
    rotate(atan2(endY-startY,endX-startX));
    fill(0,100,0);
    stroke(0,100,0);
    rect(0,0, 30, 7);
    popMatrix();
  }
}

Projectile (The object that represents the projectile launched in the game):

class Projectile{
  float xcoor, ycoor, velocity, angle, xvel, initXvel, yvel, xacc;
  static final float yacc = .5;
  boolean exists = true;
  boolean exploding = false;
   PImage explode = loadImage("explosion.png");
   float explodeframe = 0;
  float explodeX, explodeY;

  Projectile(float xcoor, float ycoor, float initVelocity, float initAngle){
    this.xcoor = xcoor;
    this.ycoor = ycoor;
    velocity = initVelocity;
    angle = initAngle;
    fill(255,69,0);
    ellipse(xcoor, ycoor, 15, 15);
    xvel = velocity * cos(angle);
    initXvel = xvel;
    yvel = velocity * sin(angle);
  }

  //returns Object that Projectile collided w/, null if no collisions.
  Object collide(){
    for(int i = 0; i < walls.length; i++){
      if(xcoor >= walls[i].leftX && ycoor >= walls[i].leftY && xcoor < walls[i].rightX){
        if(turn){
          wind = random(-.1, .1);
          xacc = wind;
          xvel = initXvel;
        }
        return walls[i];
      }
    }
      if( xcoor >= myTank.xcoor && ycoor >= myTank.ycoor && xcoor < myTank.xcoor + 60){
        if(turn){
          wind = random(-.1, .1);
          xacc = wind;
          xvel = initXvel;
        }
         return myTank;
      }
      if( xcoor >= yourTank.xcoor && ycoor >= yourTank.ycoor && xcoor < yourTank.xcoor + 60){
        if(turn){
          wind = random(-.1, .1);
          xacc = wind;
          xvel = initXvel;
      }
        return yourTank;
      }

    return null;
  }
  void move(){
    xacc = wind;
     yvel += yacc;
    xvel += xacc;
    xcoor += xvel;
    ycoor += yvel;
    fill(255,69,0);
    ellipse(xcoor, ycoor, 15, 15);
    Object obj = collide();
    if(obj != null){
      explosion.play();
      exists = false;
      explodeframe = frameCount;
      explodeX = xcoor;
      explodeY = ycoor;
      exploding = true;
    } 
    if(obj instanceof Tank){
      Tank t = (Tank) obj;
      tankExplosion.play();
      t.setExploding(true);
      t.explodeFrame = frameCount;
    }
    if((xcoor > width - 200 || xcoor < 0) || ycoor > height){
        explosion.play();
      exists = false;
      rescreen();
      if(turn){
        wind = random(-.1, .1);
        xacc = wind;
        xvel = initXvel;
    }
    }
  }

  //Called when Projectile collides


  void explode(){
  //insert explode animation
  rescreen();
  image(explode, explodeX - (frameCount - explodeframe)*8, explodeY - (frameCount - explodeframe)*8, (frameCount - explodeframe)*16, (frameCount - explodeframe)*16);
  exploding = frameCount < explodeframe + 12;
  if(frameCount > explodeframe + 12){
  }
  if(!exploding){
    rescreen();  
    exists = false;
  }
  }

}

Tank (object representing the tanks in the game):

import processing.sound.*;
class Tank{
  float xcoor, ycoor;
  boolean exploding = false;
  boolean direction; //direction tank is facing: true is right, false is left
  PImage tank;
  float explodeFrame = 0;
  PImage explode = loadImage("explosion.png");
  float numHits;
  String name;

  Tank(float xcoor, float ycoor, boolean dir, String n){
    this.xcoor = xcoor;
    this.ycoor = ycoor;
    direction = dir;
    name = n;
    if(direction){
      tank = loadImage("tankright.png");
    }
    else{
      tank = loadImage("tankleft.png");
    }
    image(tank, xcoor, ycoor, 60, 30);
  fill(0,100,0);
  stroke(0,100,0);
    if(direction){
    rect(xcoor + 33, ycoor, 30, 7);
    }else{
      rect(xcoor+ 25, ycoor, -30, 7);
    }
  }

  Projectile shoot(float velocity, float angle){
    shoot.play();
    if(direction){
    return new Projectile(xcoor+33, ycoor + 7, velocity, angle);
    }else{
      return new Projectile(xcoor + 25, ycoor + 7, velocity, angle);
    }
  }
  void redraw(){
     image(tank, xcoor, ycoor, 60, 30);
     if(!mousePressed){
        fill(0,100,0);
        stroke(0,100,0);
        if(direction){
       rect(xcoor + 33, ycoor, 30, 7);
        }
        else{
           rect(xcoor+ 25, ycoor, -30, 7);
        }
     }
  }
  void setExploding(boolean exp){
    exploding = exp;
    numHits++;

  }

  //called while the tank is still exploding
  void explode(){
  image(explode, xcoor - (frameCount - explodeFrame)*8, ycoor - (frameCount - explodeFrame)*8, (frameCount - explodeFrame)*16, (frameCount - explodeFrame)*16);
  exploding = frameCount < explodeFrame + 48;
  if(!exploding){
    if(numHits >= 5){
      /*htptanks.setVisible(false);
      htptanks.remove();
      restart.setVisible(false);
      restart.remove();
      tanks.remove();
      setupWinnerWindow(name);
      tankswindow.setVisible(false);
      paused = true;
      pauseframe = frameCount;*/
    }else{
      rescreen();
    }

  }
  }
}

Wall

class Wall{
//coordinates of top left and right coordinates, others must be bottom of window
  float leftX, leftY, rightX;

  Wall(float leftX, float leftY, float rightX){
    this.leftX = leftX;
    this.leftY = leftY;
    this.rightX = rightX;
    fill(139,69,19);
    stroke(139,69,19);
    rect(leftX, leftY, rightX-leftX, height - leftY);
  }
  void redraw(){
    fill(139,69,19);
    stroke(139,69,19);
    rect(leftX, leftY, rightX-leftX, height - leftY);
  }
}

What I’ve tried so far is “return”-ing after every action in my draw method to stop the sketch from performing two actions in one frame, and creating a “pause” as a buffer between an action happening and allowing any other action to occur. Neither of these fixed the problem

Any help would be really appreciated! This is my GitHub repo in case anyone wants to try running the game.Thank you so much!

1 Like

If you post your question to multiple sites, please link between those posts. This question has also been asked here:

https://stackoverflow.com/questions/50790186/how-can-i-switch-between-controlp5s-without-skipping-pages

1 Like