Example: A Ball that Bounces and Pulses

In these notes you will learn:

  • How to make a bouncing ball become bigger and smaller as it moves.
  • How to prevent the ball from getting “stuck” in an edge.

Introduction

In these notes we are going to being with the following program that makes a ball bounce around the screen:

float x; // (x, y) is the center of the ball
float y;
float dx; // speed along x-axis
float dy; // speed along y-axis

void setup() {
  size(500, 500);

  x = 250;
  y = 250;
  dx = 1.5;
  dy = -3.5;

  smooth();
  fill(255, 0, 0);
  noStroke();
}

void draw() {
  background(255);

  ellipse(x, y, 100, 100);

  x += dx;
  y += dy;

  // check if hit top edge
  if (y - 50 < 0) {
    dy = -dy;
  }

  // check if hit bottom edge
  if (y + 50 > 499) {
    dy = -dy;
  }

  // check if hit right edge
  if (x + 50 > 499) {
    dx = -dx;
  }

  // check if hit the left edge
  if (x - 50 < 0) {
    dx = -dx;
  }
}

Our goal here is to add a neat new effect to this program: we want the ball to pulse, i.e. continuously get smaller and bigger as it bounces around the screen.

Adding a Diameter Variable

The first thing we’ll do is add a float variable called diam that represents the diameter of the ball:

float x; // (x, y) is the center of the ball
float y;
float dx; // speed along x-axis
float dy; // speed along y-axis

float diam;

void setup() {
  size(500, 500);

  x = 250;
  y = 250;
  dx = 1.5;
  dy = -3.5;

  diam = 100;

  smooth();
  fill(255, 0, 0);
  noStroke();
}

void draw() {
  background(255);

  ellipse(x, y, diam, diam);

  x += dx;
  y += dy;

  // check if hit top edge
  if (y - diam / 2 < 0) {
    dy = -dy;
  }

  // check if hit bottom edge
  if (y + diam / 2 > 499) {
    dy = -dy;
  }

  // check if hit right edge
  if (x + diam / 2 > 499) {
    dx = -dx;
  }

  // check if hit the left edge
  if (x - diam / 2 < 0) {
    dx = -dx;
  }
}

Notice that we:

  • initialized diam too 100 in setup()
  • changed the circle-drawing statement to ellipse(x, y, diam, diam)
  • changed the condition of all the if-statements to use diam / 2 instead of 50

Be sure to test your program after making this change to make sure it worked correctly. The behaviour should be exactly the same as before.

Adding a Diameter Speed Change

We want the ball’s diameter to grow smalle and bigger at some constant rate, and so we will add another variable, called ddiam, that represents by how many pixels the diameter of the ball will change on each call to draw():

float x; // (x, y) is the center of the ball
float y;
float dx; // speed along x-axis
float dy; // speed along y-axis

float diam;
float ddiam;  // speed at which diameter changes

void setup() {
  size(500, 500);

  x = 250;
  y = 250;
  dx = 1.5;
  dy = -3.5;

  diam = 100;
  ddiam = -1;  // diameter starts getting smaller

  smooth();
  fill(255, 0, 0);
  noStroke();
}

void draw() {
  background(255);

  ellipse(x, y, diam, diam);

  x += dx;
  y += dy;
  diam += ddiam;

  if (diam < 20) {   // if the diameter is too small,
    ddiam = -ddiam;  // flip ddiam's sign so the diam gets bigger
  }

  if (diam > 100) {  // if the diameter is too big,
    ddiam = -ddiam;  // flip ddiam's sign the diam gets smaller
  }

  // check if hit top edge
  if (y - diam / 2 < 0) {
    dy = -dy;
  }

  // check if hit bottom edge
  if (y + diam / 2 > 499) {
    dy = -dy;
  }

  // check if hit right edge
  if (x + diam / 2 > 499) {
    dx = -dx;
  }

  // check if hit the left edge
  if (x - diam / 2 < 0) {
    dx = -dx;
  }
}

Here’s the changes from the previous program:

  • the float ddiam has been added to represent the rate at which the ball’s diameter changes
  • in setup(), ddiam is initialized to -1 so that the ball is initially shrinking
  • in draw(), we added the statement diam += ddiam to update the size of the ball’s diameter after ew draw it
  • again in draw(), we added two if-statements to check to flip the sign of ddiam when the ball gets either too small or too big

When you run this program it may appear, initially, that it works correctly. However, you should soon see an obvious bug: the ball sometimes gets in the edges.

Fixing the Edge Bug

The previous program doesn’t always work correctly. The problem is that, sometimes, the ball goes past an edge and gets stuck. For example, if the top- most point of the ball goes past the top edge, then even though the direction of the ball changes, the top-most point of the ball is still off the edge.

This is a tricky and subtle problem. We’ll deal with by cheating a little: whenever we detected that the ball has hit an edge, we’ll move back onto the screen to make sure that it doesn’t get caught in an edge.

float x; // (x, y) is the center of the ball
float y;
float dx; // speed along x-axis
float dy; // speed along y-axis

float diam;
float ddiam;

void setup() {
  size(500, 500);

  x = 250;
  y = 250;
  dx = 1.5;
  dy = -3.5;

  diam = 100;
  ddiam = -1;

  smooth();
  fill(255, 0, 0);
  noStroke();
}

void draw() {
  background(255);

  ellipse(x, y, diam, diam);

  x += dx;
  y += dy;
  diam += ddiam;

  if (diam < 20) {
    ddiam = -ddiam;
  }

  if (diam > 100) {
    ddiam = -ddiam;
  }

  // check if hit top edge
  if (y - diam / 2 < 0) {
    y = diam / 2;
    dy = -dy;
  }

  // check if hit bottom edge
  if (y + diam / 2 > 499) {
    y = 499 - diam / 2;
    dy = -dy;
  }

  // check if hit right edge
  if (x + diam / 2 > 499) {
    x = 499 - diam / 2;
    dx = -dx;
  }

  // check if hit the left edge
  if (x - diam / 2 < 0) {
    x = diam / 2;
    dx = -dx;
  }
}

The edge-detection if-statements now all have this form:

if the ball has hit the edge then
reset the position of the ball so it is entirely on the screen flip the sign of the appropriate d-variable

This is important! Just changing the d-variable is not enough because it doesn’t stop the ball from getting stuck in an edge.

Questions

  1. Modify the bouncing ball program so that a small ellipse is drawn under the ball as a shadow to give the illusion of 3-dimensions. For example:

    A ball with an elliptical shadow under it.

    Make sure the shadow gets bigger as the ball’s size increases.

  2. Modify the bouncing ball program so that when the ball hits the left edge of the screen, its speed increases by a factor of 1.5.

    Also, keep the ball’s size fixed: do not have it change size when it hits an edge.

  3. Write a program that makes a square bounce around the screen. When the squares hits an edge it should hit the edge exactly, without any part of it going off the screen.

  4. Write a program that makes a PImage bounce around the screen. When the image hits an edge it should hit the edge exactly, without any part of it going off the screen.

  5. Modify the bouncing ball program so that the ball changes to red when it hits the top edge; to green when it hits the right edge; to blue when it hits the bottom edge; and to orange when it hits the left edge.

  6. Draw a 200-by-200 square centered in the middle of a 500-by-500 screen. Write a program that makes a ball bounce inside this square.

  7. Make two different colored balls bounce around the screen. You’ll need to keep track of the position and velocity of each ball, so your program will need at least 8 different variables.

  8. Modify the bouncing ball program so that the ball’s diameter does not change when it hits an edge. Instead, use the map function to make the balls diameter go from 25 to 150 as the mouse pointer moves horizontally across the screen.

    For example, when the mouse pointer is halfway across the screen, the ball should have a diameter of about 87 (i.e. halfway between 25 and 150). When the mouse pointer is at the very left of the screen, the ball’s diameter should be 25.

  9. Modify the bouncing ball program so that every fifth time the ball hits any edge it changes color. For example, it can start out orange, and the after 5 edge hits it turns red. Then after 5 more edge hits it turns red, and so on.

    Also, keep the ball’s size fixed: do not have it change size when it hits an edge.

Code: The Bouncing Ball Program

float x; // (x, y) is the center of the ball
float y;
float dx; // speed along x-axis
float dy; // speed along y-axis

float diam;
float ddiam;

void setup() {
  size(500, 500);

  x = 250;
  y = 250;
  dx = 1.5;
  dy = -3.5;

  diam = 100;
  ddiam = -1;

  smooth();
  fill(255, 0, 0);
  noStroke();
}

void draw() {
  background(255);

  ellipse(x, y, diam, diam);

  x += dx;
  y += dy;
  diam += ddiam;

  if (diam < 20) {
    ddiam = -ddiam;
  }

  if (diam > 100) {
    ddiam = -ddiam;
  }

  // check if hit top edge
  if (y - diam / 2 < 0) {
    y = diam / 2;
    dy = -dy;
  }

  // check if hit bottom edge
  if (y + diam / 2 > 499) {
    y = 499 - diam / 2;
    dy = -dy;
  }

  // check if hit right edge
  if (x + diam / 2 > 499) {
    x = 499 - diam / 2;
    dx = -dx;
  }

  // check if hit the left edge
  if (x - diam / 2 < 0) {
    x = diam / 2;
    dx = -dx;
  }
}