Multi-Game Generator (different states with classes for games)

This example demo is meant to simulate a multi-game generator; each ‘game’ is represented by a simple class placed under its own tab. There is a home screen with instructions and each game is assigned a unique key. It was designed with the beginning programmer in mind and meant to serve as a basis for a much larger app.

boolean startHome = true;
boolean startGame1 = false;
boolean startGame2 = false;
boolean startGame3 = false;

Rect r;
Circle c;
Triangle t;

void setup() {
  fullScreen(P3D);
  r = new Rect(100,100,400,300);
  c = new Circle(300, 300, 400);
  t = new Triangle(300, 100, 100, 550, 550, 550);
}

void draw() {
  if (startHome) {
    showHome();
  }
  if (startGame1) {
    showGame1();
  }
  if (startGame2) {
    showGame2();
  }
  if (startGame3) {
    showGame3();
  }
}

void keyPressed() {
  if (key == '0') {
    startHome = true;
    startGame1 = false;
    startGame2 = false;
    startGame3 = false;
  }
  if (key == '1') {
    startHome = false;
    startGame1 = true;
    startGame2 = false;
    startGame3 = false;
  }
  if (key == '2') {
    startHome = false;
    startGame1 = false;
    startGame2 = true;
    startGame3 = false;
  }
  if (key == '3') {
    startHome = false;
    startGame1 = false;
    startGame2 = false;
    startGame3 = true;
  }
}

void showHome() {
  background(209);
  fill(255, 0, 0);
  textSize(42);
  textAlign(CENTER,CENTER);
  text ("Welcome to Multi Game Generator \n Home = key '0' \n Game 1 = key '1' \n"
  + " Game 2 = key '2' \n Game 3 = key '3' \n Cmd-Q to Quit", width/2, height/2);
}

void showGame1() {
  background(255);
  r.display();
}

void showGame2() {
  background(255);
  t.display();
}

void showGame3() {
  background(255);
  c.display();
}

Circle Tab Code:

class Circle {
  int x, y, d;

  Circle(int xpos, int ypos, int diameter) {
    x = xpos;
    y = ypos;
    d = diameter;
  }

  void display() {
    translate(400, 200);
    stroke(0);
    strokeWeight(5);
    fill(0, 0, 255);
    circle(x, y, d);
  }
}

Rectangle Tab Code:

class Rect {
  int x, y, w, h;

  Rect(int xpos, int ypos, int wide, int ht) {
    x = xpos;
    y = ypos;
    w = wide;
    h = ht;
  }

  void display() {
    translate(400, 200);
    strokeWeight(3);
    stroke(0);
    fill(255,0,0);
    rect(x, y, w, h);
  }
}

Triangle Tab Code:

class Triangle {
  int x1, y1, x2, y2, x3, y3;

  Triangle(int pt1x, int pt1y, int pt2x, int pt2y, int pt3x, int pt3y) {
    x1 = pt1x;
    y1 = pt1y;
    x2 = pt2x;
    y2 = pt2y;
    x3 = pt3x;
    y3 = pt3y;
  }

  void display() {
    translate(400,200);
    strokeWeight(3);
    stroke(0);
    fill(0,255,0);
    triangle(x1, y1, x2, y2, x3, y3);
  }
}
2 Likes

Thank you for that!

I developed your idea further and I am grateful for your idea.
Development of a Sketch.

First version
It occured to me that only ONE of the 4 boolean vars can be true at one time. So this reminded me of states. So I changed this.
In draw() we can use switch() now, keyPressed is shorter.
Here is the whole code in one go:

//
final int HOME=0;
int state = HOME;

Rect r;
Circle c;
Triangle t;

// ------------------------------------------------------------------------------

void setup() {
  fullScreen(P3D);

  r = new Rect(100, 100, 400, 300);
  c = new Circle(300, 300, 400);
  t = new Triangle(300, 100, 100, 550, 550, 550);
}// func

void draw() {
  switch (state) {
  case HOME:
    showHome();
    break;
  case 1:
    showGame1();
    break;
  case 2:
    showGame2();
    break;
  case 3:
    showGame3();
    break;
  }//switch
  //
}//func

// ------------------------------------------------------------------------------

void keyPressed() {
  if (key >= '0' && key <= '3') {
    state = int(key+"");
  } else if (key==ESC) {
    state=HOME; // MENU
    key=0;      // kill ESC
  } else if (key=='X') {
    exit();
  }//else if
}// func

// ------------------------------------------------------------------------------

void showHome() {
  background(209);
  fill(255, 0, 0);
  textSize(42);
  textAlign(CENTER, CENTER);
  text ("Welcome to Multi Game Generator \n Home = key '0' \n Game 1 = key '1' \n"
    + " Game 2 = key '2' \n Game 3 = key '3' \nX to Quit (shift-x)", width/2, height/2);//
}

void showGame1() {
  r.display();
}

void showGame2() {
  t.display();
}

void showGame3() {
  c.display();
}

// ===============================================================================================
//classes in Tabs

class Circle {
  int x, y, d;

  Circle(int xpos, int ypos, int diameter) {
    x = xpos;
    y = ypos;
    d = diameter;
  }

  void display() {
    background(255);
    translate(400, 200);
    stroke(0);
    strokeWeight(5);
    fill(0, 0, 255);
    circle(x, y, d);
  }
}

// ===============================================================================================
//Tab

class Rect {
  int x, y, w, h;

  Rect(int xpos, int ypos, int wide, int ht) {
    x = xpos;
    y = ypos;
    w = wide;
    h = ht;
  }

  void display() {
    background(255);
    translate(400, 200);
    strokeWeight(3);
    stroke(0);
    fill(255, 0, 0);
    rect(x, y, w, h);
  }
}

// ===============================================================================================
// Tab

class Triangle {
  int x1, y1, x2, y2, x3, y3;

  Triangle(int pt1x, int pt1y,
    int pt2x, int pt2y,
    int pt3x, int pt3y) {
    x1 = pt1x;
    y1 = pt1y;
    x2 = pt2x;
    y2 = pt2y;
    x3 = pt3x;
    y3 = pt3y;
  }

  void display() {
    background(255);
    translate(400, 200);
    strokeWeight(3);
    stroke(0);
    fill(0, 255, 0);
    triangle(x1, y1, x2, y2, x3, y3);
  }
}
//


Second version
We can also use an enum clause for state so make it better readable.
Here is the whole code in one go:


// possible states of the program - enum ! 
enum State {
  HOME, GAME1, GAME2, GAME3
}
State state = State.HOME;

Rect r;
Circle c;
Triangle t;

// ------------------------------------------------------------------------------

void setup() {
  fullScreen(P3D);

  r = new Rect(100, 100, 400, 300);
  c = new Circle(300, 300, 400);
  t = new Triangle(300, 100, 100, 550, 550, 550);
}//func

void draw() {
  switch (state) {
  case HOME:
    showHome();
    break;
  case GAME1:
    showGame1();
    break;
  case GAME2:
    showGame2();
    break;
  case GAME3:
    showGame3();
    break;
  }//switch
  //
}//func

// ------------------------------------------------------------------------------
// Inputs

void keyPressed() {
  if (key >= '0' && key <= '3') {
    int keyAsInt = int(key+""); // convert to in
    // println(keyAsInt);       // test
    state = State.values()[keyAsInt]; // convert int keyAsInt to the new state (using keyAsInt as a kind of index for State)
    // println(state.toString());     // test
  } else if (key==ESC) {
    state=State.HOME; // MENU
    key=0;      // kill ESC
  } else if (key=='X') {
    exit();
  }//else if
}//func

// ------------------------------------------------------------------------------
// Other funcs

void showHome() {
  background(209);
  fill(255, 0, 0);
  textSize(42);
  textAlign(CENTER, CENTER);
  text ("Welcome to Multi Game Generator \n Home = key '0' \n Game 1 = key '1' \n"
    + " Game 2 = key '2' \n Game 3 = key '3' \nX to Quit (shift-x)", width/2, height/2);//
}

void showGame1() {
  r.display();
}

void showGame2() {
  t.display();
}

void showGame3() {
  c.display();
}

// ===============================================================================================
//classes in Tabs

class Circle {
  int x, y, d;

  Circle(int xpos, int ypos, int diameter) {
    x = xpos;
    y = ypos;
    d = diameter;
  }

  void display() {
    background(255);
    translate(400, 200);
    stroke(0);
    strokeWeight(5);
    fill(0, 0, 255);
    circle(x, y, d);
  }
}

// ===============================================================================================
//Tab

class Rect {
  int x, y, w, h;

  Rect(int xpos, int ypos, int wide, int ht) {
    x = xpos;
    y = ypos;
    w = wide;
    h = ht;
  }

  void display() {
    background(255);
    translate(400, 200);
    strokeWeight(3);
    stroke(0);
    fill(255, 0, 0);
    rect(x, y, w, h);
  }
}

// ===============================================================================================
// Tab

class Triangle {
  int x1, y1, x2, y2, x3, y3;

  Triangle(int pt1x, int pt1y,
    int pt2x, int pt2y,
    int pt3x, int pt3y) {
    x1 = pt1x;
    y1 = pt1y;
    x2 = pt2x;
    y2 = pt2y;
    x3 = pt3x;
    y3 = pt3y;
  }

  void display() {
    background(255);
    translate(400, 200);
    strokeWeight(3);
    stroke(0);
    fill(0, 255, 0);
    triangle(x1, y1, x2, y2, x3, y3);
  }
}
//


Third version

Now it occured to me, that for each new game we need a new object variable.
Why not put them in an array?
It would be an array of classes. To make that possible, we need to make an interface first, that makes all classes compatible, so that we can have them together in one array:

interface GameInterface {

  // Let's abstract

  // Let's abstract funcs
  void showState();
  void keyPressedState();
  void mousePressedState();
}

Each class must tell us that it belongs to this Interface:

class Circle implements StateInterface {

And each class must provide the functions the Interface promises,
display(), keyPressedState() and mousePressedState().
Therefore you can go back to MENU

  • by key 0 OR by mouse in the three games.

Advantage: We can get rid of the switch() in draw() (almost) and
get rid of these:

void showGame1() {
  r.display();
}

void showGame2() {
  t.display();
}
....

Nice Advantage here.

Full Code:


// different games/states in one Sketch, version 3
// see https://discourse.processing.org/t/multi-game-generator/45139

// This version features an Interface approach for the three Game classes. Similar approach: see https://discourse.processing.org/t/scenes-in-processing/40386

// possible states of the program that we use as an Index for the array of Game-classes.
enum State {
  HOME, GAME1, GAME2, GAME3
}
State state = State.HOME;

// List of Game classes in array.
StateInterface[] games = new StateInterface[3];

// ------------------------------------------------------------------------------

void setup() {
  fullScreen(P3D);

  // Make List of Game classes
  games[0] = new Rect(100, 100, 400, 300);
  games[1] = new Circle(300, 300, 400);
  games[2] = new Triangle(300, 100, 100, 550, 550, 550);
}//func

void draw() {
  switch (state) {
  case HOME:
    showHome();  // show Menu
    break;
  default:
    // show current game
    int getIntFromState = state.ordinal()-1; // we must say -1 because 0 is Home and doesn't occur in the array (yet).
    // see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html
    games[getIntFromState].display(); // show current game
    break;
  }//switch
  //
}//func

// ------------------------------------------------------------------------------
// Inputs

void keyPressed() {
  if (state==State.HOME) {
    // run a keyPressed func for the Menu
    keyPressedForMenu();
  } else {
    // ALL GAMES are told to run their respective keyPressedState() function here (which can be different)
    int getIntFromState = state.ordinal()-1; // we must say -1 because 0 is Home and doesn't occur in the array (yet).
    // see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html
    games[getIntFromState].keyPressedState();
  }
}//func

void mousePressed() {
  // The menu doesn't have mouse, only the games
  if (state!=State.HOME) {
    int getIntFromState = state.ordinal()-1; // we must say -1 because 0 is Home and doesn't occur in the array (yet).
    // see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html
    // ALL GAMES are told to run their respective mousePressedState() function here (which can be different)
    games[getIntFromState].mousePressedState();
  }
}//func

void keyPressedForMenu() {
  if (key >= '1' && key <= '3') {
    // keys from 1 to 3 start a game
    int keyAsInt = int(key+""); // convert to int
    // println(keyAsInt);       // test
    state = State.values()[keyAsInt]; // convert int keyAsInt to the new state (using keyAsInt as a kind of index for State)
    // println(state.toString());     // test
  } else if (key==ESC) {
    state=State.HOME; // MENU
    key=0;      // kill ESC
  } else if (key=='X') {
    exit();
  }//else if
}//func

// ------------------------------------------------------------------------------
// Other funcs

void showHome() {
  background(209);
  fill(255, 0, 0);
  textSize(42);
  textAlign(CENTER, CENTER);
  text ("Welcome to Multi Game Generator \n Home = key '0' \n Game 1 = key '1' \n"
    + " Game 2 = key '2' \n Game 3 = key '3' \nX to Quit (shift-x)", width/2, height/2);//
}//func

// ===============================================================================================
// Interface in Tab

interface StateInterface {

  // Let's abstract

  // Let's abstract funcs
  void display();
  void keyPressedState();
  void mousePressedState();
}//interface

// ===============================================================================================
//classes in Tabs

class Circle implements StateInterface {
  int x, y, d;

  Circle(int xpos, int ypos, int diameter) {
    x = xpos;
    y = ypos;
    d = diameter;
  }

  void display() {
    background(255);
    translate(400, 200);
    stroke(0);
    strokeWeight(5);
    fill(0, 0, 255);
    circle(x, y, d);
  }

  void keyPressedState() {
    if (key == '0') {
      state = State.values()[0]; // the new state (using 0 as a kind of index for State)
    }
  }//method

  void mousePressedState() {
    state = State.values()[0]; // the new state (using 0 as a kind of index for State)
  }
}//class

// ===============================================================================================
//Tab

class Rect implements StateInterface {
  int x, y, w, h;

  Rect(int xpos, int ypos, int wide, int ht) {
    x = xpos;
    y = ypos;
    w = wide;
    h = ht;
  }

  void display() {
    background(255);
    translate(400, 200);
    strokeWeight(3);
    stroke(0);
    fill(255, 0, 0);
    rect(x, y, w, h);
  }

  void keyPressedState() {
    if (key == '0') {
      state = State.values()[0]; // the new state (using 0 as a kind of index for State)
    }
  }

  void mousePressedState() {
    state = State.values()[0]; // the new state (using 0 as a kind of index for State)
  }
}//class

// ===============================================================================================
// Tab

class Triangle implements StateInterface {
  int x1, y1, x2, y2, x3, y3;

  Triangle(int pt1x, int pt1y,
    int pt2x, int pt2y,
    int pt3x, int pt3y) {
    x1 = pt1x;
    y1 = pt1y;
    x2 = pt2x;
    y2 = pt2y;
    x3 = pt3x;
    y3 = pt3y;
  }

  void display() {
    background(255);
    translate(400, 200);
    strokeWeight(3);
    stroke(0);
    fill(0, 255, 0);
    triangle(x1, y1, x2, y2, x3, y3);
  }

  void keyPressedState() {
    if (key == '0') {
      state = State.values()[0]; // the new state (using 0 as a kind of index for State)
    }
  }

  void mousePressedState() {
    state = State.values()[0]; // the new state (using 0 as a kind of index for State)
  }
}//class
//

Thanks again!

Warm regards,

Chrisir

Fourth version

And finally:

It is strange to distinguish between home and games in this way (in draw() etc.) like I did.
Let’s implement the menu as a class as well and put it into the array!

Now draw() is even shorter!

And keyPressed() and mousePressed() too.

Full code:


// different games/states in one Sketch, version 4
// see https://discourse.processing.org/t/multi-game-generator/45139

// This version 4 moves the entire Menu as a class into the main array of classes. 

// (also since version 3 features an Interface approach for the three Game classes. Similar approach: see https://discourse.processing.org/t/scenes-in-processing/40386)

// possible states of the program that we use as an Index for the array of Game-classes.
enum State {
  HOME, GAME1, GAME2, GAME3
}
State state = State.HOME;

// List of Game classes in array.
StateInterface[] games = new StateInterface[4];

// ------------------------------------------------------------------------------
// Processing two core functions

void setup() {
  fullScreen(P3D);

  // Make List of Game classes
  games[0] = new Home();
  games[1] = new Rect(100, 100, 400, 300);
  games[2] = new Circle(300, 300, 400);
  games[3] = new Triangle(300, 100, 100, 550, 550, 550);
}//func

void draw() {
  // show current game (or menu)
  int getIntFromState = state.ordinal(); // we must say -1 because 0 is Home and doesn't occur in the array (yet).
  // see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html
  games[getIntFromState].display(); // show current game
}//func

// ------------------------------------------------------------------------------
// Inputs

void keyPressed() {
  // ALL GAMES (or menu) are told to run their respective keyPressedState() function here (which can be different)
  int getIntFromState = state.ordinal();
  // see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html
  games[getIntFromState].keyPressedState();
}//func

void mousePressed() {
  // The menu doesn't have mouse, only the games (but we treat the menu the same)
  int getIntFromState = state.ordinal();
  // see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Enum.html
  // ALL GAMES (or menu) are told to run their respective mousePressedState() function here (which can be different)
  games[getIntFromState].mousePressedState();
}//func
//

// ===============================================================================================
// Interface in Tab

interface StateInterface {

  // Let's abstract

  // Let's abstract display()
  void display();
  void keyPressedState();
  void mousePressedState();
}//interface

// ===============================================================================================
//classes in Tabs

class Home implements StateInterface {
  // THE MENU

  Home() {
    //empty
  }

  void display() {
    background(209);
    fill(255, 0, 0);
    textSize(42);
    textAlign(CENTER, CENTER);
    text ("Welcome to Multi Game Generator \n Home = key '0' \n Game 1 = key '1' \n"
      + " Game 2 = key '2' \n Game 3 = key '3' \nX to Quit (shift-x)", width/2, height/2);//
  }

  void keyPressedState() {
    // THE MENU
    if (key >= '1' && key <= '3') {
      // keys from 1 to 3 start a game
      int keyAsInt = int(key+""); // convert to int
      // println(keyAsInt);       // test
      state = State.values()[keyAsInt]; // convert int keyAsInt to the new state (using keyAsInt as a kind of index for State)
      // println(state.toString());     // test
    } else if (key==ESC) {
      state=State.HOME; // MENU
      key=0;      // kill ESC
    } else if (key=='X') {
      exit();
    }//else if
  }//method

  void mousePressedState() {
    //empty
  }
  //
}//class

//=====================================================================================================

class Circle implements StateInterface {
  int x, y, d;

  Circle(int xpos, int ypos, int diameter) {
    x = xpos;
    y = ypos;
    d = diameter;
  }

  void display() {
    background(255);
    translate(400, 200);
    stroke(0);
    strokeWeight(5);
    fill(0, 0, 255);
    circle(x, y, d);
  }

  void keyPressedState() {
    if (key == '0') {
      state = State.values()[0]; // the new state (using 0 as a kind of index for State)
    }
  }//method

  void mousePressedState() {
    state = State.values()[0]; // the new state (using 0 as a kind of index for State)
  }
}//class

// ===============================================================================================
//Tab

class Rect implements StateInterface {
  int x, y, w, h;

  Rect(int xpos, int ypos, int wide, int ht) {
    x = xpos;
    y = ypos;
    w = wide;
    h = ht;
  }

  void display() {
    background(255);
    translate(400, 200);
    strokeWeight(3);
    stroke(0);
    fill(255, 0, 0);
    rect(x, y, w, h);
  }

  void keyPressedState() {
    if (key == '0') {
      state = State.values()[0]; // the new state (using 0 as a kind of index for State)
    }
  }

  void mousePressedState() {
    state = State.values()[0]; // the new state (using 0 as a kind of index for State)
  }
}//class

// ===============================================================================================
// Tab

class Triangle implements StateInterface {
  int x1, y1, x2, y2, x3, y3;

  Triangle(int pt1x, int pt1y,
    int pt2x, int pt2y,
    int pt3x, int pt3y) {
    x1 = pt1x;
    y1 = pt1y;
    x2 = pt2x;
    y2 = pt2y;
    x3 = pt3x;
    y3 = pt3y;
  }

  void display() {
    background(255);
    translate(400, 200);
    strokeWeight(3);
    stroke(0);
    fill(0, 255, 0);
    triangle(x1, y1, x2, y2, x3, y3);
  }

  void keyPressedState() {
    if (key == '0') {
      state = State.values()[0]; // the new state (using 0 as a kind of index for State)
    }
  }

  void mousePressedState() {
    state = State.values()[0]; // the new state (using 0 as a kind of index for State)
  }
}//class
//

This was interesting.

The code can still be improved…

Chrisir

3 Likes