The Keyboard, the Mouse, and Gravity

In these notes you will learn:

  • How to use the keyPressed() function to do something when any key is pressed.
  • How to use the mousePressed() function to do something when a mouse button is clicked.
  • How to simulate gravity.

In this note, we will start the following program makes a red ball bounce around the screen. Notice that we’ve commented out the background(255) statement in draw() so that the ball leaves a trail as it moves:

float x, y;
float dx, dy;
float radius;

void setup() {
  size(500, 500);
  x = width / 2;
  y = height / 2;
  dx = 1;
  dy = -2.5;
  radius = 50;
}

void draw() {
  //background(255);

  noStroke();
  fill(255, 0, 0);
  ellipse(x, y, 2 * radius, 2 * radius);

  // update position
  x += dx;
  y += dy;

  //
  // check if an edge has been hit
  //

  // hit top?
  if (y - radius < 0) {
    y = radius;  // adjust position to ensure sprite is on-screen
    dy = -dy;
  }

  // hit bottom?
  if (y + radius > 499) {
    y = 499 - radius;  // adjust position to ensure sprite is on-screen
    dy = -dy;
  }

  // hit left?
  if (x - radius < 0) {
    x = radius;  // adjust position to ensure sprite is on-screen
    dx = -dx;
  }

  // hit right?
  if (x + radius > 499) {
    x = 499 - radius;  // adjust position to ensure sprite is on-screen
    dx = -dx;
  }
}

Notice that we’ve declared two variables on the same line, e.g.:

float x, y;

This is just to save a bit of typing. It is the same as if we had written it out like this:

float x;
float y;

The keyPressed() Function

Suppose we want the program to “reset” itself any time we press a key. By that we mean the screen should be cleared of any trails, and the ball should go back to its initial starting position (with its initial velocity).

The easiest way to do that is to use the built-in keyPressed() function. Add this to the bottom of the program (after the draw() function):

void keyPressed() {
  background(255);  // erase the screen

  x = width / 2;   // set the position to be the same
  y = height / 2;  // as in setup()

  dx = 1;          // set the velocity to be the same
  dy = -2.5;       // as in setup()
}

Now when you press (almost) any key on the keyboard, the screen should erase itself, and the ball should restart in the middle.

If you want to check for a specific key being pressed, use the special key variable. For example, this version of keyPressed() only resets the screen if the R key is pressed:

void keyPressed() {
    if (key == 'r') {
        background(255);  // erase the screen

        x = width / 2;   // set the position to be the same
        y = height / 2;  // as in setup()

        dx = 1;          // set the velocity to be the same
        dy = -2.5;       // as in setup()
    }
}

Be careful: if Caps Lock is on, or you press <Shift> while pressing the R key, you are generating an uppercase R, but the program only looks for a lowercase R. So if you want either both uppercare and lowercase R to cause a reset, check for both like this:

void keyPressed() {
    if (key == 'r' || key == 'R') {
        background(255);  // erase the screen

        x = width / 2;   // set the position to be the same
        y = height / 2;  // as in setup()

        dx = 1;          // set the velocity to be the same
        dy = -2.5;       // as in setup()
    }
}

In the if-statement, the symbol || means “or”. We’ll discuss ||, and other logical operators, in more detail in a future lecture.

Finally, when you press some keys, such as <Shift> or <Ctrl>, they don’t cause keyPressed() to be called. Such keys are considered special, and you should check the Processing documentation for the keyCode variable for examples of how to handle them.

The mousePressed() Function

Similar to keyPressed(), mousePressed() is a special Processing function that is called whenever you press a button (any button) on your mouse.

Suppose we want the ball to reverse direction when we click the mouse. Add this code to the end of the program:

// clicking the mouse (anywhere on the screen) reverses the ball's direction
void mousePressed() {
  dx = -dx;
  dy = -dy;
}

Now when you click the mouse, the ball will instantly reverse direction.

Lets implement a different feature. Now lets have the mouse clicks control whether the ball is moving or stopped. If the ball is moving, then clicking the mouse should stop it, and if the ball is stopped, clicking the mouse should make it start moving again. Here’s a solution:

void mousePressed() {
  if (dx == 0 && dy == 0) {  // ball is stopped, so start it moving
    dx = 1;
    dy = -2.5;
  } else {                   // ball is moving, so stop it
    dx = 0;
    dy = 0;
  }
}

An if-statement is used to distinguish between the case when the ball is stopped, and when the ball is moving. In the if-statement condition dx == 0 && dy == 0, && means “and”, and so the entire thing is true when both dx and dy are equal to 0. That is, when the ball is stopped.

The else part indicates the code that will run if the condition dx == 0 && dy == 0 evaluates to false.

We’ll learn (much!) more about the details of if-statements and logical expressions in a future lecture.

Gravity

Getting an object to appear to be affected by gravity is the basis for many interesting visual effect. We won’t worry about the exact physics of gravity here. As long as the ball looks like it is under the influence of gravity we’ll be satisfied.

Note

Of course, sometimes it is important to simulate gravity accurately, or other physical processes, even in video games. This is tricky enough that many games use so-called physics engines that provide pre-made functions designed for efficiently simulating realistic physics. As you can imagine, creating a physics engine needs a lot more knowledge of physics than we will cover in this introductory programming course.

If you are curious about this topic, then you might want to look at a book such as Game Physics Engine Development by Ian Millington.

A Bouncing Ball in Gravity

Gravity is a force that we are all familiar with: it pulls objects down towards the ground, causing them to accelerate. For instance, on Earth, if you drop a ball its speed will increase at a rate of 9.8 m/s each second it falls.

So lets write a Processing program that simulates a ball that bounces due to gravity. The main trick is to add a small, constant, number to dy (the speed of the ball in the y-direction) on each call to draw(). This will cause the ball to accelerate downwards — which makes it look as if gravity is pulling it down.

We will also simulate the elasticity of the ball, which tells us how high it will bounce after it hits the ground. Every time the ball hits the ground it loses some energy, and so it bounces lower and lower. Exactly how much energy the ball loses depends on what it is made of, e.g. a rubber ball loses less energy than a glass marble.

We are ignoring many details, such as the ball’s mass and air friction. But as you will see when you run the program, the results are good enough for many purposes.

Here is the program:

float x, y;
float dx, dy;
float radius;
float gravity_y;
float elasticity;

void setup() {
  //
  // ... same as starting program ...
  //

  // gravity and elasticity are constant values: try changing them
  gravity_y = 0.1;

  // elasticity < 1 makes the ball bounce lower each time
  // elasticity > 1 makes the ball bounce higher each time
  elasticity = 0.5;
}

void draw() {
  //
  // ... same as starting program ...
  //

  dy += gravity_y;

  // when the ball bounces, make it lose some speed so that
  // it doesn't bounce as high next time
  if (y + radius > 499) {
    y = 499 - radius;
    dy = -(dy * elasticity);
  }

  //
  // ... same as starting program ...
  //
}

Adding More Gravity

On Earth, gravity only pulls things downwards. However, we can simulate other kinds of gravity.

For example, just for fun lets add a second force of gravity that causes things to move towards the right edge of the screen:

float x, y;
float dx, dy;
float radius;
float gravity_x, gravity_y;
float elasticity;

void setup() {
  size(500, 500);
  x = width / 2;
  y = height / 2;
  dx = 0.1;
  dy = -0.5;
  radius = 50;

  gravity_x = 1.25;
  gravity_y = 0.1;

  // elasticity < 1 makes the ball bounce lower each time
  // elasticity > 1 makes the ball bounce higher each time
  elasticity = 0.5;
}

void draw() {
  background(255);

  stroke(255);
  fill(255, 0, 0);
  ellipse(x, y, 2 * radius, 2 * radius);

  // update position
  x += dx;
  y += dy;

  dx += gravity_x;
  dy += gravity_y;

  // when the ball bounces, make it lose some speed so that
  // it doesn't bounce as high next time
  if (x + radius > 499) {
    x = 499 - radius;
    dx = -dx*0.8;
    //    dx = -(dx * elasticity);
  }

  if (y + radius > 499) {
    y = 499 - radius;
    dy = -(dy * elasticity);
  }

  //
  // check if an edge has been hit
  //

  // hit top?
  if (y - radius < 0) {
    y = radius;  // adjust position to ensure sprite is on-screen
    dy = -dy;
  }

  // hit bottom?
  if (y + radius > 499) {
    y = 499 - radius;  // adjust position to ensure sprite is on-screen
    dy = -dy;
  }

  // hit left?
  if (x - radius < 0) {
    x = radius;  // adjust position to ensure sprite is on-screen
    dx = -dx;
  }

  // hit right?
  if (x + radius > 499) {
    x = 499 - radius;  // adjust position to ensure sprite is on-screen
    dx = -dx;
  }
}

The movement of the ball is now much more complex. Try commenting-out the call to background to see the path the ball follows.

Questions

  1. What are some keys on a typical keyboard that, when pressed, will not caused keyPressed() to be called?
  2. What is the return type of the mousePressed() function? What are it’s input parameters?
  3. Besides certain video games, what is another application that might require more accurate modelling of physics?
  4. What is the purpose of the variable elasticity?

Programming Questions

  1. The notes provide code for mousePressed() that made the ball start/stop moving whenever the mouse is clicked. One of the problems with it is that it always re-starts the ball with same velocity, i.e. up and to the left. This can look pretty strange when the ball was just moving in some other direction.

    Fix this problem: modify the code in mousePressed() so that when the ball is stopped and the user clicks the mouse, the ball starts moving with the exact same velocity (speed and direction) it was moving with before it was stopped.

    Hint: Add two new variables, prev_dx and prev_dy, to keep track of the velocity of the ball after it is stopped.

  2. Modify the the gravity demo program so that the ball stops when it hits the right edge of the screen instead of rolling off.

  3. Modify the the gravity demo program to use an image (of your choice) instead of a circle. Make sure no part of the image goes off the screen when it bounces.