Hit Detection and Functions¶
In these notes you will learn:
- What boolean functions are.
- How to test if a point is in a circle.
- How to test if a point is in a rectangle.
Introduction¶
In this note we will see how to do basic hit detection. The general problem is to determine when two shapes touch, or intersect, each other. For example, in a graphical user interface you need to know which window the mouse pointer is in when it is clicked. Or in a first-person shooter video game you want to know when a bullet has hit another player.
Hit detection turns out to be a surprisingly complex topic if you are dealing with arbitrary shapes. Thus, we will restrict ourselves to a couple of the most important cases involving circles and rectangles.
Boolean Functions¶
Suppose you want to test if a number, x
, is negative. You could do that
with this function:
boolean isNegative(float x) {
if (x < 0) {
return true;
} else {
return false;
}
}
isNegative
returns either the value true
, or the value false
.
These two values are the boolean values, and so we call a function that
returns booleans boolean
a boolean function.
Note
Sometimes boolean functions are called predicates, or test functions. In this course, we will stick with the term boolean function.
You can use it like this:
if (isNegative(x)) {
println("Can't take the square root of a negative number.");
} else {
println(sqrt(x));
}
The body of the isNegative
function consists of an if/else structure.
However, some programmers prefer writing it is this way:
boolean isNegative(float x) {
return x < 0;
}
This is much shorter! The expression x < 0
is a boolean
expression
that evaluates to either true
or false
, depending upon the value of
x
. If x < 0
is true
, then x
must be negative. If x < 0
is
false
, then x
is either 0 or positive.
Testing if a Point is in a Circle¶
Suppose you are given a point (a, b), and a circle with center (x, y) and radius r. How can you tell if (a, b) is inside the circle, or outside the circle?
As shown in the diagram on the right, the trick is to measure the distance between (x, y) and (a, b). If it’s r, or less, then (a, b) is in the circle; otherwise it’s outside the circle.
The Processing function dist(x, y, a, b)
returns the distance between (x,
y) and (a, b). We can use it like this:
if (dist(x, y, a, b) <= r) {
// (a, b) is in the circle
// do something!
}
Note
dist(x, y, a, b)
calculates the distance using this formula:
If dist
didn’t already exist, you could could calculate the distance
between (x, y) and (a, b) like this:
sqrt((x - a) * (x - a) + (x - b) * (x - b))
Let’s write it as a boolean function to make it easy to re-use:
//
// Returns true if (a, b) is in the circle with center (x, y) and with
// radius r; otherwise it returns false
//
boolean pointInCircle(float a, float b, // (a, b) is any point
float x, float y, // (x, y) is the circle's center
float r // r is the circle's radius
)
{
if (dist(a, b, x, y) <= r) {
return true;
} else {
return false;
}
}
For example, here’s a program that changes a bouncing ball’s color to red when the mouse pointer hovers over it:
class Sprite {
float x;
float y;
float dx;
float dy;
}
//
// Returns true if (a, b) is in the circle with center (x, y) and with
// radius r; otherwise it returns false
//
boolean pointInCircle(float a, float b, // (a, b) is any point
float x, float y, // (x, y) is the circle's center
float r // r is the circle's radius
)
{
if (dist(a, b, x, y) <= r) {
return true;
} else {
return false;
}
}
color ball_color;
Sprite ball = new Sprite();
float diam;
void setup() {
size(500, 500);
smooth();
// set the ball's initial position and velocity
ball.x = 250;
ball.y = 250;
ball.dx = 1.3;
ball.dy = -2.77;
diam = 50;
ball_color = color(0, 255, 0);
}
void draw() {
// re-draw the background
background(255);
// draw the ball
noStroke();
fill(ball_color);
ellipse(ball.x, ball.y, diam, diam);
// move the ball to its next position
ball.x += ball.dx;
ball.y += ball.dy;
// hit the top edge?
if (ball.y < 0) {
ball.y = 0;
ball.dy = -ball.dy;
}
// hit the bottom edge?
if (ball.y > 499) {
ball.y = 499;
ball.dy = -ball.dy;
}
// hit the left edge?
if (ball.x < 0) {
ball.x = 0;
ball.dx = -ball.dx;
}
// hit the right edge?
if (ball.x > 499) {
ball.x = 499;
ball.dx = -ball.dx;
}
// set the ball's color to red if the mouse
// pointer is over the ball, and green otherwise
if (pointInCircle(mouseX, mouseY, ball.x, ball.y, diam / 2)) {
ball_color = color(255, 0, 0);
} else {
ball_color = color(0, 255, 0);
}
}
Notice that we don’t need to click the mouse pointer for the ball to change color. All that matters in this program is that the pointer “hover” over the ball.
Clicking the Ball¶
Lets modify our program so that the ball changes color just when we click it.
To do this we need some way to recognize mouse-clicks. One way is to use the
special mousePressed()
function:
// comment-out the if-statement at the end of draw() before adding
// this function
void mousePressed() {
ball_color = color(255, 0, 0);
}
As the comment says, when you add this to the program above you should
comment-out the if-statement at the end of draw()
.
Every time you click a mouse button, Processing automatically calls
mousePressed()
and runs whatever code is in its body. In this case the
ball will become red whenever you click the mouse button.
It doesn’t (yet) matter where the mouse pointer is when you click it:
mousePressed()
only takes mouse-clicks into account, and does not care
about the location of the pointer.
What if we want the color to change just when we click inside the ball?
The solution is to move the if-statement that we commented-out in draw()
into mousePressed()
:
void mousePressed() {
if (pointInCircle(mouseX, mouseY, ball.x, ball.y, diam / 2)) {
ball_color = color(255, 0, 0);
}
}
Now, when the user clicks a mouse button, mousePressed()
first checks if
the mouse pointer is inside the ball. If it is, the ball’s color is set to
red. Otherwise, it does nothing.
Testing if a Point is in a Rectangle¶
Suppose you want to test if a point (a, b) is inside a rectangle of width Width, height Height, and with upper-left corner (x, y).
In the diagram, (x, y) is the upper-left corner of a rectangle with dimensions Width-by-Height. The green point (a, b), it is inside the rectangle because it is both between the two vertical edges, and it is also between the two horizontal edges. In contrast, the point (c, d) is not inside the rectangle because it is not between the vertical edges (although it is between the horizontal edges).
The trick for testing if (a, b) is inside the rectangle is to do two separate “between” tests: first, test if (a, b) is between the left/right edges, and then test if it is between the top/bottom edges.
Suppose we’ve drawn a rectangle on the screen with this statement:
rect(x, y, Width, Height);
How can we tell if the point (a
, b
), is inside this rectangle? From
the discussion above we need to check that:
a
is between the left and right edgesb
is between the top and bottom edges
To test if a
is between the left and right edges, we can use this
expression:
x <= a && a <= x + Width
Recall that &&
means “and” in Processing, and so this expression is true
just when both x <= a
and a <= x + Width
are true.
Similarly, to test if b
is between the top and bottom edges of the
rectangle, we can do this:
y <= b && b <= y + Height
Now if the (a, b) is inside the rectangle, then both expressions must be
true
, i.e. this big boolean expression must be true
:
(x <= mouseX) && (mouseX <= x + Width)
&&
(y <= mouseY) && (mouseY <= y + Height)
Note
We’ve written the &&
on its own line to emphasize it. In Processing
extra spaces, or blank lines, around symbols usually don’t matter. This
helps us format the code in whatever is easiest for humans to read.
Now lets put this test into a boolean function to make it easier to re-use:
//
// returns true if point (a, b) is the rectangle with upper-left
// corner (x, y), and width w and height h
//
boolean pointInRect(float a, float b, // any point
float x, float y, // rectangle's upper-left corner
float w, float h) // width and height
{
if ((a >= x && a <= x + w) && (b >= y && b <= y + h)) {
return true;
} else {
return false;
}
}
This function requires 6 input values, which is a lot! We’ve grouped the related variables on lines an added comments to help clarify what they are for.
Many experienced programmers would write pointInRect
like this:
boolean pointInRect(float a, float b, float x, float y, float w, float h)
{
return (a >= x && a <= x + w) && (b >= y && b <= y + h);
}
You can write it whichever way you prefer, but you should be able to read and understand both.
Lets write a demonstration program that makes a rectangle change color when we click on it. This program is very similar to the one with the ball:
class Sprite {
float x;
float y;
float dx;
float dy;
}
Sprite ball = new Sprite();
float Width, Height;
color rect_color = color(255, 0, 0);
void setup() {
size(500, 500);
smooth();
// set the rectangle's initial position and velocity
ball.x = 250;
ball.y = 250;
ball.dx = 1.3;
ball.dy = -2.77;
Width = 75;
Height = 50;
}
void draw() {
// re-draw the background
background(255);
// draw the rectangle
noStroke();
fill(rect_color);
rect(ball.x, ball.y, Width, Height);
// move the rectangle to its next position
ball.x += ball.dx;
ball.y += ball.dy;
// hit the top edge?
if (ball.y < 0) {
ball.y = 0;
ball.dy = -ball.dy;
}
// hit the bottom edge?
if (ball.y > 499) {
ball.y = 499;
ball.dy = -ball.dy;
}
// hit the left edge?
if (ball.x < 0) {
ball.x = 0;
ball.dx = -ball.dx;
}
// hit the right edge?
if (ball.x > 499) {
ball.x = 499;
ball.dx = -ball.dx;
}
}
void mousePressed() {
if (pointInRect(mouseX, mouseY, ball.x, ball.y, Width, Height)) {
if (rect_color == color(0, 255, 0)) {
rect_color = color(255, 0, 0);
}
else {
rect_color = color(0, 255, 0);
}
}
}
Questions¶
What is a
boolean
function?For each of the following English descriptions, write an equivalent boolean expression in Processing. You don’t need to write a function, just an expression.
Test if a number
x
is positive:boolean isPositive(float x)
Test if a number
x
is 0:boolean isZero(float x)
Test if a number
x
is both greater than, or equal to, 0, and less than, or equal to, 100:boolean inRange(float x)
Test if a number
xx
is the square of another numberx
. For example, ifxx
is 25 andx
is 5, then your function should returntrue
.boolean isSquareOf(float xx, float x)
Explain the
dist
function. Give an example of how to call it.Consider the following statement:
a = (x == 1);
Assuming
a
has been defined, is this a legal statement in Processing? If so, what type of variable isa
, and what is it’s value?Consider a rectangle that is 180 pixels wide and 265 pixels high with upper-left corner at (100, 205). Write a boolean expression that is true just when the mouse pointer is inside this rectangle.
Programming Questions¶
Modify the bouncing rectangle program so that it draws two rectangles that touch just at one corner, e.g.:
Further, change the program so that when the user clicks inside either rectangle, then both rectangles change color.