17.0. Computer Graphics - RaindropCatcher Game
Now that we know some graphics basics, let's write a game!
This is a game that I made up, similar to Whack-a-Mole. It's called RaindropCatcher
.
Here's what happens. In the game window, a single raindrop at a time "falls" at a random location on the screen. The player tries to "catch" the raindrop by clicking on it. At first the raindrops are pretty big and easy to catch, but as time goes by, the rain will fall more quickly, and the raindrops get smaller, making them more difficult to catch. Players get a point for each catch, and are only allowed two misses. On the third miss, the game is over.
This is a relatively simple game to code if you have a lot of programming experience. If you're relatively new to programming, however, this game poses some interesting challenges.
We'll go through the development of this game step by step, testing each new feature as we go.
17.1. Setting up the Game
First, let's just see if we can make some "raindrops" (actually circles) appear on the screen at random locations.
Part 1. Making Raindrops
Use the template given here to help you in writing this first part of the game.
/** * Raindrop Catcher Game * @author Richard White * @version 2021-07-12 */ function setup() { // This function runs once when the program first runs // Use it to set up all the variables that you want to // use. // set up canvas to a screen size that works // turn off stroke for raindrops // set max diameter for a raindrop // set counter for score // set counter for misses // establish the size for the default font textSize(28); } function draw() { // This function runs over and over, ~ 30x / second // set the canvas background to a color you like // set up new raindrop // get a random X value based on the width of screen // get a random Y value based on the height of screen // establish diameter of the new drop // set the fill color for the raindrop (blue?) // draw a circle at that location }
If you get stuck, show/hide one version.
/** * Raindrop Catcher Game * @author Richard White * @version 2021-07-12 */ function setup() { // This function runs once when the program first runs // Use it to set up all the variables that you want to // use. // set up canvas to a screen size that works createCanvas(600, 400); // turn off stroke for raindrops noStroke(); // set max diameter for a raindrop maxDiameter = 100; // set counter for score score = 0; // set counter for misses misses = 0; // establish the size for the default font textSize(28); } function draw() { // This function runs over and over, ~ 30x / second // set the canvas background to a color you like background(200); // set up new raindrop // get a random X value based on the width of screen randX = random(width); // get a random Y value based on the height of screen randY = random(height); // establish diameter of the new drop diameter = maxDiameter; // set the fill color for the raindrop (blue?) fill(0, 0, 255); // draw a circle at that location circle(randX, randY, diameter); }
Cool! We have raindrops falling!
They're falling way too quickly for our game, but that's okay. We're going to take care of that as we work through the next versions of the program.
17.2. Clicking on Raindrops
We're going to modify the program that we've already written with a couple of new features that will help make our game work. Mostly, we need to make sure that the user has the ability to "catch" (click on) a raindrop.
Part 2. Clicking on Raindrops
Use the template given here to help you add the clicking-on-a-raindrop feature of the game. (Sections to modify are highlighted in bold.)
/** * Raindrop Catcher Game * @author Richard White * @version 2021-07-12 */ function setup() { // This function runs once when the program first runs // Use it to set up all the variables that you want to // use. // set up canvas to a screen size that works createCanvas(600, 400); // turn off stroke for raindrops noStroke(); // set max diameter for a raindrop maxDiameter = 100; // set counter for score score = 0; // set counter for misses misses = 0; // establish the size for the default font textSize(28); // set boolean variable for existence of drop to false } function mouseClicked() { // This function is called whenever the user clicks the mouse. // We need to check to see if they've clicked within the body of // the raindrop. // If they tried to click on drop, set dropExists to false } function draw() { // This function runs over and over, ~ 30x / second // set the canvas background to a color you like background(200); // if a drop doesn't exist, create one if (!dropExists) { // get a random X value based on the width of screen randX = random(width); // get a random Y value based on the height of screen randY = random(height); // establish diameter of the new drop diameter = maxDiameter; // update value of dropExists } // set the fill color for the raindrop (blue?) fill(0, 0, 255); // draw a circle at that location circle(randX, randY, diameter); }
If you get stuck, show/hide one version.
/** * Raindrop Catcher Game * @author Richard White * @version 2021-07-12 */ function setup() { // This function runs once when the program first runs // Use it to set up all the variables that you want to // use. // set up canvas to a screen size that works createCanvas(600, 400); // turn off stroke for raindrops noStroke(); // set max diameter for a raindrop maxDiameter = 100; // set counter for score score = 0; // set counter for misses misses = 0; // establish the size for the default font textSize(28); // set boolean variable for existence of drop to false dropExists = false; } function mouseClicked() { // This function is called whenever the user clicks the mouse. // We need to check to see if they've clicked within the body of // the raindrop. // NOTE: There's actually a bug in the code here that makes this // function a little off. Can you figure out what it is? // If they click, confirm that raindrop exists, and then see if // they got the raindrop or not. if ( dropExists ) { if ( mouseX > randX - diameter && mouseX < randX + diameter && mouseY > randY - diameter && mouseY < randY + diameter ) { score = score + 1; } else { misses = misses + 1; } // If they tried to click on drop, set dropExists to false dropExists = false; } } function draw() { // This function runs over and over, ~ 30x / second // set the canvas background to a color you like background(200); // if a drop doesn't exist, create one if (!dropExists) { // get a random X value based on the width of screen randX = random(width); // get a random Y value based on the height of screen randY = random(height); // establish diameter of the new drop diameter = maxDiameter; // update value of dropExists dropExists = true; } // set the fill color for the raindrop (blue?) fill(0, 0, 255); // draw a circle at that location circle(randX, randY, diameter); }
Cool! We have raindrops and we can click on them.
We need to be able to keep track of our points, though. On to the next part.
17.3. Displaying Score
We've been keeping score, but how do we display it?
Let's take a look to see how that all works.
Part 3. Displaying Points
Use the code given here to display the points in the game.
/**
* Raindrop Catcher Game
* @author Richard White
* @version 2021-07-12
*/
function setup()
{
// This function runs once when the program first runs
// Use it to set up all the variables that you want to
// use.
// set up canvas to a screen size that works
createCanvas(600, 400);
// turn off stroke for raindrops
noStroke();
// set max diameter for a raindrop
maxDiameter = 100;
// set counter for score
score = 0;
// set counter for misses
misses = 0;
// establish the size for the default font
textSize(28);
// set boolean variable for existence of drop to false
dropExists = false;
}
function mouseClicked()
{
// This function is called whenever the user clicks the mouse.
// We need to check to see if they've clicked within the body of
// the raindrop.
// NOTE: There's actually a bug in the code here that makes this
// function a little off. Can you figure out what it is?
// If they click, confirm that raindrop exists, and then see if
// they got the raindrop or not.
if ( dropExists )
{
if (
mouseX > randX - diameter &&
mouseX < randX + diameter &&
mouseY > randY - diameter &&
mouseY < randY + diameter
)
{
score = score + 1;
}
else
{
misses = misses + 1;
}
// If they tried to click on drop, set dropExists to false
dropExists = false;
}
}
function draw()
{
// This function runs over and over, ~ 30x / second
// set the canvas background to a color you like
background(200);
// if a drop doesn't exist, create one
if (!dropExists)
{
// get a random X value based on the width of screen
randX = random(width);
// get a random Y value based on the height of screen
randY = random(height);
// establish diameter of the new drop
diameter = maxDiameter;
// update value of dropExists
dropExists = true;
}
// set the fill color for the raindrop (blue?)
fill(0, 0, 255);
// draw a circle at that location
circle(randX, randY, diameter);
// display the score on the screen
text("Score: " + str(score), 10, height - 20);
// display the misses on the screen
text("Misses: " + str(misses), width - 220, height - 20);
}
Cool! We have raindrops and we can click on them to earn points.
On to the next part.
17.4. Shrinking Raindrops
To make the game a little more exciting, let's have the raindrops "dry up" (disappear) over time. To make the game more dynamic and visually interesting, we're going to make each raindrop shrink in size, so the player will have to move quickly to click on the raindrop before it disappears.
So, the diameter
of the raindrop is going to be changing. In order for us to keep track of the maximum size of the raindrop each time, we'll create a new variable, diameterMax
, that will keep track of the largest size of each raindrop.
Our strategy to make the drop shrink in size is this: every time through the draw()
method we'll reduce the sized of the diameter
by some amount.
This is a little tricky because we now have another way that the player can "miss" a drop—by not clicking on it at all. We can check that by looking at the diameter, and if it every drops to 0, we'll consider that a miss, and reset for a new raindrop.
Let's see how all of that can work.
Part 4. Shrinking Raindrops
Use the template given here to implement the shrinking raindrops component of the game.
/**
* Raindrop Catcher Game
* @author Richard White
* @version 2021-07-12
*/
function setup()
{
// This function runs once when the program first runs
// Use it to set up all the variables that you want to
// use.
// set up canvas to a screen size that works
createCanvas(600, 400);
// turn off stroke for raindrops
noStroke();
// set max diameter for a raindrop
maxDiameter = 100;
// set boolean variable for existence of drop to false
dropExists = false;
// set initial score variable to 0
score = 0;
// set initial misses variable to 0
misses = 0;
// establish the size for the default font
textSize(28);
}
function mouseClicked()
{
// This function is called whenever the user clicks the mouse.
// We need to check to see if they've clicked within the body of
// the raindrop.
// NOTE: There's actually a bug in the code here that makes this
// function a little off. Can you figure out what it is?
// If they click, confirm that raindrop exists, and then see if
// they got the raindrop or not.
if ( dropExists )
{
if (
mouseX > randX - diameter &&
mouseX < randX + diameter &&
mouseY > randY - diameter &&
mouseY < randY + diameter
)
{
score = score + 1;
}
else
{
misses = misses + 1;
}
// If they tried to click on drop, set dropExists to false
dropExists = false;
}
}
function draw()
{
// This function runs over and over, ~ 30x / second
// set the canvas background to a color you like
background(200);
// if a drop doesn't exist, create one
if (!dropExists)
{
// get a random X value based on the width of screen
randX = random(width);
// get a random Y value based on the height of screen
randY = random(height);
// establish diameter of the new drop
diameter = maxDiameter;
// update value of dropExists
dropExists = true;
}
// set the fill color for the raindrop (blue?)
fill(0, 0, 255);
// draw a circle at that location
circle(randX, randY, diameter);
// display the score on the screen
text("Score: " + str(score), 10, height - 20);
// display the misses on the screen
text("Misses: " + str(misses), width - 220, height - 20);
// shrink the raindrop by some amount to make it harder
// to hit over time
// if raindrop has gotten too small, they missed it, and
// kill this raindrop
}
If you get stuck, show/hide one version.
/** * Raindrop Catcher Game * @author Richard White * @version 2021-07-12 */ function setup() { // This function runs once when the program first runs // Use it to set up all the variables that you want to // use. // set up canvas to a screen size that works createCanvas(600, 400); // turn off stroke for raindrops noStroke(); // set max diameter for a raindrop maxDiameter = 100; // set initial diameter for this raindrop diameter = maxDiameter; // set boolean variable for existence of drop to false dropExists = false; // set initial score variable to 0 score = 0; // set initial misses variable to 0 misses = 0; // establish the size for the default font textSize(28); } function mouseClicked() { // This function is called whenever the user clicks the mouse. // We need to check to see if they've clicked within the body of // the raindrop. // NOTE: There's actually a bug in the code here that makes this // function a little off. Can you figure out what it is? // If they click, confirm that raindrop exists, and then see if // they got the raindrop or not. if ( dropExists ) { if ( mouseX > randX - diameter && mouseX < randX + diameter && mouseY > randY - diameter && mouseY < randY + diameter ) { score = score + 1; } else { misses = misses + 1; } // If they tried to click on drop, set dropExists to false dropExists = false; } } function draw() { // This function runs over and over, ~ 30x / second // set the canvas background to a color you like background(200); // if a drop doesn't exist, create one if (!dropExists) { // get a random X value based on the width of screen randX = random(width); // get a random Y value based on the height of screen randY = random(height); // establish diameter of the new drop diameter = maxDiameter; // update value of dropExists dropExists = true; } // set the fill color for the raindrop (blue?) fill(0, 0, 255); // draw a circle at that location circle(randX, randY, diameter); // display the score on the screen text("Score: " + str(score), 10, height - 20); // display the misses on the screen text("Misses: " + str(misses), width - 220, height - 20); // shrink the raindrop by some amount to make it harder // to hit over time diameter = diameter - 1; // if raindrop has gotten to small, they missed it, and // kill this raindrop if (diameter <= 0) { misses = misses + 1; dropExists = false; } }
17.5. Three Strikes, You're Out
All good things come to an end, and our game will, too, eventually. Let's say we only give the player three misses before we stop the game.
This shouldn't be difficult at all.
Part 5. End the game after three misses
At the bottom of the draw()
loop, include this bit of code that will bring the game to a halt at the appropriate time.
if (misses >= 3) { background(255); text("Score: " + str(score), 10, height - 20); text("Misses: " + str(misses), width - 220, height - 20); text("Game Over!", width / 2 - 90, height / 2); noLoop(); }
17.6. Increasing Difficulty Over Time
How can we make the game get progressively more difficult as users play? ;)
We already make each raindrop harder to hit over the short term by making it shrink over the course of a second or so.
What if we also make the raindrop harder to hit over the longer term by making the initial size (maximumSize
) less as the game proceeds.
How quickly should maximumSize
decrease? Where do we put the code to make that happen?