Multiple Screens¶
Most programs consist of multiple screens. For example, video games usually have, at least, an introductory screen, a play screen, and a “game over” screen. In this note we’ll see a general-purpose way to handle different kinds of screens in the same program.
Handling Multiple Screens¶
The essential trick we’re going to use for handling multiple screens is to use
a variable called screen
that keeps track of what the current screen is:
String screen;
This is sometimes known as a state variable, because indicates (part of) the current state of the program.
Any particular program will a list of screens. For instance, lets imagine a game that has an intro screen, a play screen (where the main game is played), and a game-over screen displayed at the end. We structure the program like this:
String screen;
// ... other variables ...
void setup() {
size(500, 500);
screen = "intro"; // initial screen
// ...
}
void draw() {
if (screen.equals("intro")) {
// ... code for drawing the intro screen ...
} else if (screen.equals("playing")) {
// ... code for drawing the main game screen ...
} else if (screen.equals("gameover")) {
// ... code for drawing the game over screen ...
} else {
println("Error: " + screen + " is not a valid screen");
}
}
void mouseClicked() {
if (screen.equals("intro")) {
// ... code for handling mouse clicks on the intro screen ...
} else if (screen.equals("playing")) {
// ... code for handling mouse clicks on the playing screen ...
} else if (screen.equals("gameover")) {
// ... code for handling mouse clicks on the game-over screen ...
} else {
println("Error: " + screen + " is not a valid screen");
}
}
Now that we have more than one screen, every time we call functions like
draw
and mouseClicked
we need to first determine what screen we are
on. Once we know the value of screen
, we can then execute the appropriate
code.
Notice that we use .equals
(and not ==
!) to test if two strings are
the same. Case matters when you .equals
, and so "intro"
and
"Intro"
will be considered different.
Note
In practice, making screen
a String
variable can cause performance
problems if you have a lot of different screens. Since draw()
is called 30-60 times a second, constantly having to check the value of screen
can cause slow-downs. So a couple of other approaches are often used:
- Make
screen
of typeint
instead ofString
. Each screen has a code number, e.g. the intro screen could be 1, the playing screen 2, and the game-over screen 3. This speeds of the checking done by the if-else-if statements, but it now forces you, the programmer, to remember what each code number means. In big programs, this is not always easy. - Use user-defined enumeration variables. These combine the performance of
int
variables with the readability ofString
s. It also has the nice feature that it enables the compiler to catch spelling errors in the names of screens. While it does introduce new syntax, but it is probably the best solution in general.
Example: Three Simple Screens¶
Now lets use the template program above to implement a simple multi-screen program that simply displays the screen name on each screen, and then changes screens when you click the mouse:
String screen;
PFont font;
void setup() {
size(500, 500);
screen = "intro"; // initial screen
font = loadFont("AndaleMono-48.vlw");
textFont(font);
}
void draw() {
if (screen.equals("intro")) {
background(100);
fill(255, 0, 0);
text("Intro screen", 25, 50);
}
else if (screen.equals("playing")) {
background(200);
fill(0, 255, 0);
text("Playing screen", 25, 50);
}
else if (screen.equals("gameover")) {
background(255);
fill(0, 0, 255);
text("Game over screen", 25, 50);
}
else {
println("Error: " + screen + " is not a valid screen");
}
}
void mouseClicked() {
if (screen.equals("intro")) {
screen = "playing";
}
else if (screen.equals("playing")) {
screen = "gameover";
}
else if (screen.equals("gameover")) {
// do nothing
}
else {
println("Error: " + screen + " is not a valid screen");
}
}
When you run this program it first displays “Intro screen”. That’s because
screen
is initialized to the value "intro"
in setup, and so the big
if-else-if statement in draw
selects the code to print “Intro screen”.
Whenever you click a mouse button, mouseClicked
is automatically called.
The if-else-if statement within in it then selects the code that should be run
for the current screen. For example, if you click the mouse button anywhere on
the intro screen, then screen = "playing"
is called, which causes the
screen become the main playing screen. Thus, the next time draw
is called,
a new message is printed.
Individual Draw Functions¶
Now that we have multiple screens, the draw()
can get very long. The
problem with long functions is that they can be quite hard to read, and so
hard to understand and modify. So a good thing to do is to create one drawing
function for each screen in our program:
String screen;
PFont font;
void setup() {
size(500, 500);
screen = "intro"; // initial screen
font = loadFont("AndaleMono-48.vlw");
textFont(font);
}
void draw() {
if (screen.equals("intro")) {
draw_intro();
}
else if (screen.equals("playing")) {
draw_playing();
}
else if (screen.equals("gameover")) {
draw_gameover();
}
else {
println("Error: " + screen + " is not a valid screen");
}
}
void draw_intro() {
background(100);
fill(255, 0, 0);
text("Intro screen", 25, 50);
}
void draw_playing() {
background(200);
fill(0, 255, 0);
text("Playing screen", 25, 50);
}
void draw_gameover() {
background(255);
fill(0, 0, 255);
text("Game over screen", 25, 50);
}
void mouseClicked() {
if (screen.equals("intro")) {
screen = "playing";
}
else if (screen.equals("playing")) {
screen = "gameover";
}
else if (screen.equals("gameover")) {
// do nothing
}
else {
println("Error: " + screen + " is not a valid screen");
}
}
The draw()
function is now much simpler and easier to read: it makes the
flow of the program easier to understand because it doesn’t force you to
understand the details of how the particular screen is drawn. The specific
code for each screen is put into its
More Complex Screens¶
You can do more than just print messages on the screen, of course. In the following program, the “playing” screen shows a ball that bounces around the screen:
String screen;
color white = color(255);
color orange = color(255, 165, 0);
float x, y;
float dx, dy;
float diam;
PFont font;
void setup() {
size(500, 500);
screen = "intro"; // initial screen
font = loadFont("AndaleMono-48.vlw");
textFont(font);
smooth();
x = 100;
y = 100;
dx = 1;
dy = 2;
diam = 50;
}
void draw() {
if (screen.equals("intro")) {
draw_intro();
}
else if (screen.equals("playing")) {
draw_playing();
}
else if (screen.equals("gameover")) {
draw_gameover();
}
else {
println("Error: " + screen + " is not a valid screen");
}
}
void mouseClicked() {
if (screen.equals("intro")) {
screen = "playing";
}
else if (screen.equals("playing")) {
screen = "gameover";
}
else if (screen.equals("gameover")) {
// do nothing
}
else {
println("Error: " + screen + " is not a valid screen");
}
}
void draw_intro() {
background(white);
fill(255, 0, 0);
text("Ball Demo", 50, 50);
}
void draw_playing() {
background(white);
noStroke();
fill(orange);
ellipse(x, y, diam, diam);
x += dx;
y += dy;
// hit the left edge?
if (x - diam / 2 <= 0) {
dx = -dx;
x = diam / 2;
}
// hit the right edge?
if (x + diam / 2 >= 499) {
dx = -dx;
x = 499 - diam / 2;
}
// hit the top edge?
if (y - diam / 2 <= 0) {
dy = -dy;
diam += 10;
y = diam / 2;
}
// hit the bottom edge?
if (y + diam / 2 >= 499) {
dy = -dy;
y = 499 - diam / 2;
}
}
void draw_gameover() {
background(white);
fill(255, 0, 0);
text("Demo Over!", 50, 50);
}
The draw_playing
function contains all the code for making a ball bounce
around the screen. It is essentially cut-and-pasted from previous bouncing-
ball programs we’ve written.
Programming Questions¶
Write a program with the following screens and behaviors:
- An intro screen that says “Intro: click to continue”. When the user clicks the mouse on this screen, it changes to the playing screen.
- A playing screen that says “Playing: click to continue”. When the user clicks the mouse on this screen, it changes to the game-over screen.
- A game-over screen that says “Game over: click to continue”. When the user clicks the mouse on this screen, it changes to the intro screen.
Make sure all the text messages are fully on the screen.
Modify the program in the previous question so that the screens change either when the mouse is clicked or when any key is pressed.
Modify the previous program so that screen changes are random. That is, when the mouse is clicked or a button is pressed, the next screen should be chosen at random from the entire set of screens (not including the current screen).