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¶
- What are some keys on a typical keyboard that, when pressed, will not
caused
keyPressed()
to be called? - What is the return type of the
mousePressed()
function? What are it’s input parameters? - Besides certain video games, what is another application that might require more accurate modelling of physics?
- What is the purpose of the variable
elasticity
?
Programming Questions¶
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
andprev_dy
, to keep track of the velocity of the ball after it is stopped.Modify the the gravity demo program so that the ball stops when it hits the right edge of the screen instead of rolling off.
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.