PFS! Computer Science

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?