Show Navigation

# 8: Collision Detection

## Illuminations

#### Homework Questions?

• A6: Visualizer Redux (3/28/12)
• In your team, review each others programming projects from the last assignment
• Select one good example project to share with the rest of the class using the criteria:
1. Completeness
2. Person has not shared before
3. Extra features or creativity

## 8.1: Collision Detection: Asteroids

### Learner Outcomes

At the end of the lesson the student will be able to:

• Paint background scenes in a world
• Simulate firing bullets in a scenario
• Test for collisions in Greenfoot

### 8.1.1: About Asteroids

• Asteroids is a video arcade game released in 1979 by Atari
• At that time, most games were produced for and played on large arcade boxes like this one:

• Asteroids was a very popular game and spawned three arcade sequels
• In addition, several competitors produced Asteroids clones
• Versions have since been released for PlayStation, Nintendo 64 and Windows, among others

#### Asteroids Scenario

• The book scenario is a recreation of an Asteroids game
• Lets download and install this scenario of the game:

Scenario file: asteroids-2b.zip.

• Save the file to a convenient location like the Desktop.
• Start Greenfoot and open the scenario (Greenfoot unzips the file for us)
• Before we continue, lets review some key concepts from the chapter

#### Concept Review

• Bounding box: the enclosing rectangle of an image
• Collision detection: detecting the intersection of two or more objects
• Superclass type: an object can be both its own type and the type of its superclasses
• Casting: specifying a more precise type for an object than the one the compiler knows about

#### Core Game Mechanics

• Core game mechanics are the actions in a game that the player performs repeatedly
• Sometimes these are referred to as rules
• In a video game, the rules are enforced by the allowed gameplay
• Hopefully, the core game mechanics are enjoyable for the player

#### Check Yourself

1. The invisible rectangle around an image used to detect collisions in known as a ________ box.
2. True or false: a subclass of `Actor`, like `Bug`, can also be called an `Actor` because `Actor` is the superclass type.
3. What are the core game mechanics of the Asteroids game?

### 8.1.2: Painting the Background

• The Asteroids scenario is different from previous scenarios in that the world does not have a background image
• Instead, the scenario creates its own background image
• When we do not specify a background image for a world, Greenfoot uses a default white background
• We want a different color than white for outer space

#### Changing the Color

• To change colors we need to first import the Java `Color` class:
`import java.awt.Color;`
• Then we can use Java to paint the background:
```GreenfootImage background = getBackground();
background.setColor(Color.BLACK);
background.fill();
```
• Notice that the background is actually a `GreenfootImage`
• Thus we can use methods of the GreenfootImage class to work with the background

#### Painting Stars

• After painting the background black, the constructor calls the method `createStars()`
• Let us step through each line of the method which is shown below
• Notice that stars are drawn using the methods we discussed in lesson 6.2.3: Drawing Shapes
• However, we are drawing multiple shapes onto a larger image
• Also note that we can draw a gray color by using equal amounts of red, green and blue

#### Method `createStars()`

```private void createStars(int number)
{
GreenfootImage background = getBackground();
for (int i = 0; i < number; i++)
{
int x = Greenfoot.getRandomNumber(getWidth());
int y = Greenfoot.getRandomNumber(getHeight());
int color = 120 - Greenfoot.getRandomNumber(100);
background.setColor(new Color(color,color,color));
background.fillOval(x, y, 2, 2);
}
}
```

#### Check Yourself

1. The default background color of a Greenfoot world is ________.
2. The purpose of the following lines of code from `createStars()` is to generate ________ coordinates.
```int x = Greenfoot.getRandomNumber(getWidth());
int y = Greenfoot.getRandomNumber(getHeight());
```
3. The following lines of code generates random colors that are shades of ________.
```int color = 120 - Greenfoot.getRandomNumber(100);
background.setColor(new Color(color,color,color));
```

### 8.1.3: Turning and Moving

• Turning and moving uses techniques we have discussed in the past
• The `checkKeys()` method of `Rocket` responds to key presses by the user
• Turning occurs when the user presses the left or right arrow keys
• To review how to use the rotation methods see 1.4.6: Creating an Actor

#### Method `checkKeys()` of `Rocket`

```private void checkKeys()
{
ignite(Greenfoot.isKeyDown("up"));

if (Greenfoot.isKeyDown("left"))
{
setRotation(getRotation() - 5);
}
if (Greenfoot.isKeyDown("right"))
{
setRotation(getRotation() + 5);
}
if (Greenfoot.isKeyDown("space"))
{
fire();
}
}
```

#### Moving Forward

• The rocket accelerates forward when the user presses the up-arrow key
• Method `ignite()` selects the rocket image and applies the acceleration as needed
• The acceleration is applied with a `Vector` like we used in lesson 7.1.2

#### Method `ignite()` of `Rocket`

```private void ignite(boolean boosterOn)
{
if (boosterOn)
{
setImage(rocketWithThrust);
addForce(new Vector(getRotation(), 0.3));
}
else
{
setImage(rocket);
}
}
```

#### Screen Wrap Around

• One interesting feature of the rocket movement is how it wraps around the screen
• Screen wrap around was a feature of the original Asteroids game
• The `move()` method of `SmoothMover` has changed to support this feature
• Can you spot the changes in the code shown below?

#### Method `move()` of `SmoothMover`

```public void move()
{
exactX = exactX + movement.getX();
exactY = exactY + movement.getY();
if(exactX >= getWorld().getWidth()) {
exactX = 0;
}
if(exactX < 0) {
exactX = getWorld().getWidth() - 1;
}
if(exactY >= getWorld().getHeight()) {
exactY = 0;
}
if(exactY < 0) {
exactY = getWorld().getHeight() - 1;
}
super.setLocation((int) exactX, (int) exactY);
} ```

#### Check Yourself

1. When the user presses the left or right arrow keys, the rocket rotates ________ degrees.
2. When the user presses the up-arrow key, the value ________ is passed to the `ignite()` method in the following code:
`ignite(Greenfoot.isKeyDown("up"));`
3. True or false: if the `ignite()` method is called from the `checkKeys()` method, which is in turn called from act(), then the ignite method() is called every game cycle.
4. For the following vector created in the `ignite()` method, the direction is the same as the `Actor` and the length is ________.
```addForce(new Vector(getRotation(), 0.3));
```
5. Describe the screen wrap-around concept in English.

### 8.1.4: Firing Bullets

• Method `checkKeys()` calls one other method:
```if (Greenfoot.isKeyDown("space"))
{
fire();
}
```
• The `fire()` method creates a new bullet and launches it from the rocket
• Notice how bullets fire only after a delay and not continuously
• The delay is controlled by the counting variable `reloadDelayCount` with the same technique we used in Exercise 7.3:
1. Declare an instance variable to hold counting values and assign it a starting value
`private int reloadDelayCount;`
2. Change the value of the count in the `act()` method
`reloadDelayCount++;`
3. Use an `if`-statement to test if the count has reached its goal
`if (reloadDelayCount >= gunReloadTime)`
• This is a common pattern for controlling timing over multiple scenario cycles

#### Method `fire()` of `Rocket`

```private void fire()
{
if (reloadDelayCount >= gunReloadTime)
{
Bullet bullet = new Bullet(
getMovement().copy(), getRotation());
getWorld().addObject(bullet, getX(), getY());
bullet.move();
reloadDelayCount = 0;
}
}
```

#### The Flight of the Bullet

• The `Bullet` constructor adds 15 to the speed of the bullet so it moves faster than the rocket
• The bullet disappears after 30 `act()` method calls with the counting technique describe above
• If the bullet collides with an asteroid, the asteroid takes a hit
• When hit, the asteroid breaks into two smaller pieces
• If the asteroid was already small, it disappears instead

#### Check Yourself

1. The three parts to controlling timing over multiple scenario cycles are:
1. ________
2. ________
3. ________
2. Find the lines numbers of each of the three steps for controlling timing in the `Bullet` class.

### 8.1.5: Collisions and Explosions

• Oftentimes we want objects in a game to react when then collide
• We saw this in the scenario when the rocket collides with an asteroid
• The most commonly used technique is the idea of a bounding box
• Bounding boxes are an invisible box around an image
• The box is usually about the same size as the entire image as shown below:

• As you can see, bounding boxes are not perfect
• The transparent parts of an image may overlap before the collision takes place
• Despite its problems, bounding boxes continue to be used because the computation is relatively fast and easy
• Greenfoot has three methods in the `Actor` class that check for intersection of bounding boxes
• The following example from the asteroids scenario in the textbook shows how to use one of these methods

#### Methods of the Actor Class to Detect Intersections of Objects

Method Description
getIntersectingObjects() Returns a list of `Actor` objects that intersect this object.
getOneIntersectingObject() Returns a single `Actor` that intersects this object.
intersects(Actor other) Returns true if the specified `Actor` intersects, otherwise returns false.

#### Example of Collision Detection

```private void checkCollision()
{
Actor a = getOneIntersectingObject(Asteroid.class);
if (a != null)
{
World world = getWorld();
world.addObject(new Explosion(), getX(), getY());
world.removeObject(this);
}
}
```

#### Realistic Collisions

• To make collisions look realistic, we need to minimize the transparent pixels around the outside of an image
• If a bounding box is not enough then we need other collision detection techniques
• We will look at some other collision detection techniques in the future

#### Check Yourself

1. True or false: When using bounding boxes, transparent pixels are ignored during collision detection.
2. True or false: Bounding boxes are always accurate because all images are rectangles.
3. True or false: To improve the accuracy of bounding box collision detection, leave transparent pixels around the image.
4. True or false: Checking a bounding box for collisions is a relatively fast computation.

### 8.1.6: Game Over and Casting

• Lets take a look at keeping score
• The scenario has a `ScoreBoard` class, of which we can create an object
• As we can see, the score board should be shown when the game is over
• To track the game over condition, the `Space` class has a `gameOver()` method:
```public void gameOver()
{
// TODO: show the score board here. Currently missing.
}
```
• We can do many things when the game is over, including showing the score
• To show the score board when the game is over, we construct a new `ScoreBoard` object
`ScoreBoard sb = new ScoreBoard(999);`
• Then we add the new `ScoreBoard` object to the world
```addObject(sb, getWidth() / 2, getHeight() / 2);
```
• For now we are using a dummy number, which we will fix later

#### Calling `gameOver()`

• Now we need to call the `gameOver()` method at the right time
• One possible time is when the rocket hits an asteroid and explodes
• We can add this call to the `checkCollision()` method of `Rocket` like in the following code
• However, we get an error if we do

cannot find symbol - method gameOver()

#### Method `checkCollision()` calling `gameOver()`

```private void checkCollision()
{
Actor a = getOneIntersectingObject(Asteroid.class);
if (a != null)
{
World world = getWorld();
world.addObject(new Explosion(), getX(), getY());
world.removeObject(this);
world.gameOver(); // error: will not compile
}
}
```

#### Casting Needed

• The problem with the above code is that `World` does not have a `gameOver()` method
• However, `Space` has a `gameOver()` and `getWorld()` does return the subclass `Space`
• So we change `World` to `Space` and recompile:
`Space world = getWorld();`
• Now we get a different error:

incompatible types - found greenfoot.World but expected Space

• The problem in this case is that `getWorld()` returns a `Space` object but the compiler thinks it is only a `World` object
• To solve the problem, we need to tell the compiler explicitly that we have a `Space` object
• We tell the compiler by using a cast:
`Space world = (Space) getWorld();`
• Note that casting does not change the type of the object
• Casting just provides the compiler with more information

#### Check Yourself

1. True or false: `getWorld()` returns the current subclass of world as a `World` object.
2. True or false: casting permanently changes one type of object into another type
3. The following code causes a compiler error:
`Space world = getWorld();`
Enter the code to correct the problem.

answer

### Exercise 8.1

In this exercise, we implement collision detection for the rocket in Asteroids. Also, we implement a simple scoring system.

#### Specifications

1. Download the following file and save it in the scenario folder of Greenfoot.
2. Scenario file: asteroids-2b.zip.

3. Start Greenfoot and open the scenario.

Greenfoot unzips the file for us.

4. Open the `Rocket` class and add the line shown in bold to following method:
```private void checkCollision()
{
Actor a = getOneIntersectingObject(Asteroid.class);
if (a != null)
{
World world = getWorld();
world.addObject(new Explosion(), getX(), getY());
world.removeObject(this);
world.gameOver(); // error: will not compile
}
}
```
5. Refer to section 8.1.6 and change the code so that it compiles and runs correctly.

When running correctly, the rocket explodes when it hits an asteroid. If you have problems, ask a classmate or instructor for help as needed.

6. Open the `Space` class and locate the `gameOver()` method, and add the following code:
```int finalScore = scoreCounter.getValue();
ScoreBoard sb = new ScoreBoard(finalScore);
addObject(sb, getWidth() / 2, getHeight() / 2);
removeObjects(getObjects(Bullet.class));
```

Note that the last line removes bullets from the scenario so that no more asteroids are destroyed after the rocket explodes.

7. Compile the class and run the scenario with your changes and verify it works correctly.

When running correctly, the Game Over Screen appears after the rocket hits an asteroid. Resolve any problems you find, getting help from a classmate or the instructor as needed.

8. Add a method to the `Space` class to add to the score, like:
```public void addToScore(int amount)
{
scoreCounter.add(amount);
}
```
9. In the `Asteroid` class, locate the `hit()` method and add a call to the `addToScore()` method at the start of the method, like:
```Space space = (Space) getWorld();
space.addToScore(1);
```

Note that if you call `addToScore()` at the end of the `hit()` method, you may get a `NullPointerException` error. Can you see why?

10. Compile and run your scenario to verify the score changes when an asteroid is hit.

If you have problems, ask a classmate or the instructor for help as needed.

11. Save your scenario as you may find it useful in completing the next programming project.

Only submit the final scenario with all the lesson exercises completed, not the intermediate scenarios.

As time permits, read the following sections and be prepared to answer the Check Yourself questions in the section: 8.1.7: Review.

### 8.1.7: Review

Answer these questions to check your understanding. You can find more information by following the links after the question.

1. Asteroids is a video arcade game released in _______. (8.1.1)
2. What are core game mechanics of Asteroids? (8.1.1)
3. What is the default background color of a Greenfoot world? (8.1.2)
4. What is the purpose of the following lines of code from `createStars()`? (8.1.2)
```int x = Greenfoot.getRandomNumber(getWidth());
int y = Greenfoot.getRandomNumber(getHeight());
```
5. What does the following lines of code do? (8.1.2)
```int color = 120 - Greenfoot.getRandomNumber(100);
background.setColor(new Color(color,color,color));
```
6. Which of the following statements rotates the actor 5 degrees? (8.1.3)
1. `setRotation(getRotation() + 5);`
2. `setRotation(5);`
3. `setRotation(getRotation(), 5);`
4. `setRotation(getRotation() = 5);`
7. Fact or fiction: a boolean parameter accepts one of two values, either `true` or `false`. (8.1.3)
8. True or false: screen wrap around moves the actor from one side of the screen to the other when the actor's location exceeds the screen boundaries. (8.1.3)
9. Which of the following is NOT part of controlling timing of multiple scenario cycles? (8.1.4)
1. Declare a counter instance variable.
2. Change the value of the counter in the `act()` method.
3. Use an `if`-statement to test if the counter has reached its goal.
4. Call the `move()` method of the actor.
10. What is a bounding box? (8.1.5)
1. A collision between two objects.
2. The box that is drawn around an object.
3. A highlighted area of an image.
4. An imaginary box around an image.
11. True or false: When using bounding boxes, transparent pixels are ignored during collision detection. (8.1.5)
12. True or false: Bounding boxes are always accurate because all images are rectangles. (8.1.5)
13. True or false: To improve the accuracy of bounding box collision detection, leave transparent pixels around the image. (8.1.5)
14. True or false: Checking a bounding box for collisions is a relatively fast computation. (8.1.5)
15. For the following statements, what is the name of the technique for the code inside the parenthesis? (8.1.6)
```World world = /* reset of statement here */
Space space = (Space) world;
```
1. Altering
2. Casting
3. Changing
4. Redefining
16. True or false: `getWorld()` returns the current subclass of world as a `World` object. (8.1.6)
17. True or false: casting changes one type of object into another type (8.1.6)

## 8.2: Proton Waves

### Learner Outcomes

At the end of the lesson the student will be able to:

• Write code to cache images and other data
• Make use of static variables
• Animate multiple images of many game cycles
• Interact with objects in a range

### 8.2.1: More Firepower!

• The Asteroids scenario has support for a second weapon: the proton wave
• The proton wave, once fire, radiates outward from the rocket
• When the wave touches an asteroid, it damages or destroys the asteroid
• Since it works in all directions at once, it is a more powerful weapon

#### The Proton Wave Scenario

• Lets download and install the proton wave scenario:

Scenario file: asteroids-3.zip.

• Save the file inside the Greenfoot scenarios folder.
• Start Greenfoot and open the scenario (Greenfoot unzips the file for us)
• We can see the power of the proton wave in action by placing one in the scenario
• As you can see, the wave starts small and grows larger

#### How the Proton Wave Works

• Looking at the ProtonWave source code, we find three key methods
• `initializeImages()` creates the images for the expanding wave
• `grow()` manages the growing of the wave and removing it when it reaches full size
• `checkCollision()` detects and explodes all the asteroids intersected by the wave
• We will take a look at how the proton wave works in more detail in the following sections

#### Check Yourself

1. The second weapon for the rocket in Asteroids is a ______ ______.
2. True or false: the proton wave grows, destroying all asteroids in its path.
3. The three key methods of the `ProtonWave` class are ________, ________ and ________.

### 8.2.2: Initializing Images

• The `initializeImages()` method creates 30 images of the wave from small to large
• The images are stored in an array variable:
```private static GreenfootImage[] images;
```
• The reason for creating and storing the images is so the program can serve the images faster when needed
• Images are large blocks of data and image manipulation takes large amounts of computer power
• Storing images, or other data, for faster retrieval later is known as caching
• The array, or other container, that stores the data is known as a cache
• We can see how the array stores images in the following figure from the textbook

#### Method `initializeImages()`

```public static void initializeImages()
{
if (images == null)
{
GreenfootImage baseImage =
new GreenfootImage("wave.png");
images = new GreenfootImage[NUMBER_IMAGES];
for (int i = 0; i < NUMBER_IMAGES; i++)
{
int size = (i + 1) * (baseImage.getWidth()
/ NUMBER_IMAGES);
images[i] = new GreenfootImage(baseImage);
images[i].scale(size, size);
}
}
}
```

#### Notes on the Code

• The 30 images are created by first loading a base image: wave.png
• Then the `for`-loop uses the `scale()` method from `GreenfootImage` to change the size

scale(width, height): changes this image to a new size.

• The size is adjusted by multiplying the base size by (index + 1) / 30
• Notice the test for the `images` array:
`if (images == null)`
• The test condition ensures the major part of the method is executed only once

#### Use of `static` Keyword

• Notice that the `images` array is declared `static`
```private static GreenfootImage[] images;
```
• We used the keyword `static` for constants in lesson 6.1.4
• Using `static` means that the array is part of the class and is shared by all `ProtonWave` objects, which saves memory space
• If you want to share data between all objects then declare the variable `static`
• Since the `images` array is `static`, the `initializeImages()` method can be `static` as well
• However, making the `initializeImages()` method `static` is not required
• Methods declared `static` cannot access instance variables, only `static` member variables

#### Check Yourself

1. Storing data so that future requests for the data can be served faster is known as ________.
2. True or false: the `images` array variable stores all the image data.
3. True or false: the array of `GreenfootImages` referred to by the `images` variable stores all the image data.
4. True or false: each image of the proton wave is stored at various locations in main computer memory.
5. The `GreenfootImage` method that changes the size of an image is ________.
6. True or false: static member variables can be accessed by static methods
7. True or false: instance member variables can be accessed by static methods

### 8.2.3: Growing the Wave

• Now that we have the images ready, we can animate the proton wave
• The animation is an effect that happens over multiple scenario cycles
• What are the three parts to controlling timing over multiple scenario cycles?
• Note that the `grow()` method is called from the `act()` method
• As you can see, the `grow()` method follows the same pattern we discussed in lesson 8.1.4

#### Method `grow()`

```private void grow()
{
if (imageCount >= NUMBER_IMAGES)
{
getWorld().removeObject(this);
}
else
{
setImage(images[imageCount]);
imageCount++;
}
}
```

#### Smallest to Largest

• The images in the array are arranged from smallest to largest
• For every `act()` call, the `grow()` method is called and increments the `imageCount` variable
• After the wave shows its largest size, the `ProtonWave` object is removed from the world

#### Check Yourself

1. The three parts to controlling timing over multiple scenario cycles are:
1. ________
2. ________
3. ________
2. The array images are arranged from ________ to ________.
3. How often is the `grow()` method called? answer

### 8.2.4: Interacting with Objects in Range

• We looked at how the proton wave grows
• Now we need to look at how it detects and destroys the asteroids
• To detect the asteroids, the code calls the method:

getObjectsInRange(int radius, Class cls): Returns a list of objects within a given radius of the calling `Actor`.

• The method will return a list of all the actors within a given radius, like with radar
• As with other collision detection methods, you can restrict the type to a single class of actors such as `Asteroid`
• Once we get the list of objects in range, we process the list with a loop
• Each asteroid on the list gets its `hit()` method called to apply damage
• We can see how the method is used in the following code

#### Method `checkCollision()`

```private void checkCollision()
{
int range = getImage().getWidth() / 2;
List<Asteroid> asteroids =
getObjectsInRange(range, Asteroid.class);

for (Asteroid a : asteroids) {
a.hit(DAMAGE);
}
}
```

#### Breaking Up

• The `hit()` method call the `breakUp()` method
• If the size is not too small, the `breakUp()` method executes the following code
• The code replaces the current asteroid with two smaller asteroids
• First the code calculates a direction and speed for the new asteroids
```int r = getMovement().getDirection()
+ Greenfoot.getRandomNumber(45);
double l = getMovement().getLength();
Vector speed1 = new Vector(r + 60, l * 1.2);
Vector speed2 = new Vector(r - 60, l * 1.2);
```
• Then it constructs the new asteroids and adds them to the world
```Asteroid a1 = new Asteroid(size/2, speed1);
Asteroid a2 = new Asteroid(size/2, speed2);
getWorld().addObject(a1, getX(), getY());
getWorld().addObject(a2, getX(), getY());
```
• Finally, the code removes the current asteroid from the world
```getWorld().removeObject(this);
```

#### Code From Method breakUp()

```int r = getMovement().getDirection()
+ Greenfoot.getRandomNumber(45);
double l = getMovement().getLength();
Vector speed1 = new Vector(r + 60, l * 1.2);
Vector speed2 = new Vector(r - 60, l * 1.2);
Asteroid a1 = new Asteroid(size/2, speed1);
Asteroid a2 = new Asteroid(size/2, speed2);
getWorld().addObject(a1, getX(), getY());
getWorld().addObject(a2, getX(), getY());
a1.move();
a2.move();

getWorld().removeObject(this);
```

#### Check Yourself

1. True or false: The method `getObjectsInRange()` returns a list of actors within a specified range
2. In the `checkCollision()` method, the loop used to process the list of `Asteroid` objects is officially known as the ________ for loop, but most people call it the ________ loop.
3. True or false: the `breakUp()` method will replace the current asteroid with two smaller asteroids as long as the asteroids have not gotten too small.

### 8.2.5: Adding Reload Delay

• The proton wave could be fired at any time
• However, the game would be boring if we could continuously use a proton wave
• To improve the game play, `Rocket` adds a delay to the firing of the proton wave
• The code involved in firing a proton wave is shown below
• What do you notice about the pattern of the code?
• Notice that the `protonReloadTime` variable is a constant (see lesson 6.1.4)

#### `Rocket` Code to Fire a Proton Wave

```private static final int protonReloadTime = 500;
private int protonDelayCount;

public void act()
{
// Other code omitted
protonDelayCount++;
}

private void startProtonWave()
{
if (protonDelayCount >= protonReloadTime)
{
ProtonWave wave = new ProtonWave();
getWorld().addObject (wave, getX(), getY());
protonDelayCount = 0;
}
}
```

#### Check Yourself

1. What are the three parts to controlling the reload delay over multiple scenario cycles?
2. True or false: the `protonReloadTime` variable sets the number of act() cycles before the proton wave can fire again.
3. The variable `protonReloadTime` is hard to differentiate from other variables. A better coding style to make its purpose more clear would be to use all ________ letters in its name.

### Exercise 8.2

In this exercise, we add a proton wave recharge indicator.

#### Specifications

1. Download the following file and save it in the scenario folder of Greenfoot.
2. Scenario file: asteroids-3.zip.

3. Start Greenfoot and open the scenario.

Greenfoot unzips the file for us.

4. Create a new actor named `StatusBar` without an image.

For more information see lesson: 1.4.6: Creating an Actor.

5. Add an import statement to the new class to import the `Color` library.
```import java.awt.Color;
```
6. Add a constant for the default color.
```private static final Color DEFAULT_COLOR =
new Color(0, 255, 0, 128);
```
7. Add instance variables to store the data for the status bar.
```private Color barColor = DEFAULT_COLOR;
private GreenfootImage image;
private int width;
private int height;
private int percentFilled;
```
8. Add a method named `makeImage()` that makes a rectangular image of size `width` by `height` containing a bar controlled by the `percentFilled` variable:
```private void makeImage()
{
image = new GreenfootImage(width, height);
image.setColor(Color.WHITE);
image.drawRect(0, 0, width - 1, height - 1);
int barWidth = (int) (percentFilled / 100.0
* (width - 2));
image.setColor(barColor);
image.fillRect(1, 1, barWidth , height - 2);
setImage(image);
}
```
9. Add two constructors to the class that call the `makeImage()` method:
```public StatusBar()
{
this (100, 25);
}

public StatusBar(int newWidth, int newHeight)
{
width = newWidth;
height = newHeight;
makeImage();
}
```
10. Add a method named `setPercentageFilled()` to set the `percentFilled` variable:
```public void setPercentageFilled(int percentageFilled)
{
if (percentageFilled < 0)
{
percentageFilled = 0;
}
else if (percentageFilled > 100)
{
percentageFilled = 100;
}
if (percentFilled != percentageFilled)
{
percentFilled = percentageFilled;
makeImage();
}
}
```
11. Add a method named `setBarColor()` to set the color of the status bar:
```public void setBarColor(Color newBarColor)
{
if (newBarColor == null)
{
newBarColor = DEFAULT_COLOR;
}
if (!barColor.equals(newBarColor))
{
barColor = newBarColor;
makeImage();
}
}
```
12. Add two get-methods for accessing the width and height:
```public int getWidth() {
return width;
}

public int getHeight() {
return height;
}
```
13. Compile the class and verify there are no errors.

If you have problems, compare your work to the StatusBar.java file or ask a classmate or the instructor for help as needed.

14. Open the `Rocket` class and add an import statement to import the `Color` library.
```import java.awt.Color;
```
15. In the `Rocket` class and add the following variable:
```private StatusBar protonLoad;
```
16. Also in the `Rocket` class and add the following method:
```public void addedToWorld(World world)
{
protonLoad = new StatusBar(80, 20);
protonLoad.setPercentageFilled(100);
protonLoad.setBarColor(new Color(0, 255, 255, 128));
int x = world.getWidth()
- protonLoad.getWidth() / 2 - 10;
int y = world.getHeight()
- protonLoad.getHeight() / 2 - 10;
world.addObject(protonLoad, x, y);
}
```
17. Finally, in the `Rocket` class, add the following statement to the `act()` method:
```protonLoad.setPercentageFilled(protonDelayCount / 5);
```
18. Compile and run your updated scenario and verify there are no errors.

If you have problems, ask a classmate or the instructor for help as needed.

19. Save a copy of your scenario as we will be adding to it in the next exercise.

Only submit the final scenario with all the lesson exercises completed, not the intermediate scenarios.

As time permits, read the following sections and be prepared to answer the Check Yourself questions in the section: 8.2.6: Review.

### 8.2.6: Review

Answer these questions to check your understanding. You can find more information by following the links after the question.

1. Storing data so that future requests for the data can be served faster is known as __________. (8.2.2)
2. True or false: a GreenfootImage array stores all the image data in the reference variable. (8.2.2)
3. True or false: the array of `GreenfootImages` referred to by the reference variable stores all the image data. (8.2.2)
4. True or false: each image of an array of images is stored at various locations in main computer memory. (8.2.2)
5. The `GreenfootImage` method that changes the size of an image is __________. (8.2.2)
6. True or false: static member variables can be accessed by static methods. (8.2.2)
7. True or false: instance member variables can be accessed by static methods. (8.2.2)
8. The three parts to controlling timing over multiple scenario cycles are: (8.2.3)
1. _____________________________
2. _____________________________
3. _____________________________
9. True or false: The method `getObjectsInRange()` returns a list of actors within a specified range. (8.2.4)
10. Rewrite the following for-loop as an enhanced for loop: (8.2.4)
```for (int i = 0; i < rocks.size(); i++)
{
Asteroid rock = rocks.get(i);
rock.hit(DAMAGE);
}
```
11. The variable `protonReloadTime` is hard to differentiate from other variables. What would be a better coding style to make its purpose more clear? (8.2.5)

## 8.3: Scoring and Text

### Learner Outcomes

At the end of the lesson the student will be able to:

• Write code to keep track of game scores
• Display text and numbers in a scenario

### 8.3.1: Counting the Score

• Asteroids has a simple scoring keeping system implemented in the `Counter` class
• The `Counter` class displays the text "Score: " followed by a number
• As the score changes, the number displayed changes to keep up
• Let us look at the code for the `Counter` class

#### Constructing the Data

• Starting at the top, we see one constant and 4 variables
```private static final Color textColor =
new Color(255, 180, 150);

private int value = 0;
private int target = 0;
private String text;
private int stringLength;
```
• The `value` is the number displayed and `target` is the actual score
• The variable `text` is the string displayed before the number
• The variable `stringLength` is the width of the text in pixels and is calculated in the constructor

#### Constructors for Class `Counter`

```public Counter()
{
this("");
}

public Counter(String prefix)
{
text = prefix;
stringLength = (text.length() + 2) * 10;

setImage(new GreenfootImage(stringLength, 16));
GreenfootImage image = getImage();
image.setColor(textColor);

updateImage();
}
```

#### Initializing a Counter

• The job of the constructor is to initialize variables, which this one does
• As part of the initialization, the constructor creates a new `GreenfootImage`:
```setImage(new GreenfootImage(stringLength, 16));
```
• The variable `stringLength` is the width of the image
• Looking at the last line of the constructor, we see it calls a method:
`updateImage();`
• The `updateImage()` method shows the key steps to displaying text in Greenfoot
• Looking at this method, what steps do you see?

#### Method `updateImage()` Displays Text

```private void updateImage()
{
GreenfootImage image = getImage();
image.clear();
image.drawString(text + value, 1, 12);
}
```

#### Check Yourself

1. For the following code snippet, the value of `stringLength` is ________.
```scoreCounter = new Counter("Score: ");
...
public Counter(String prefix)
{
text = prefix;
stringLength = (text.length() + 2) * 10;
// rest of class omitted
```
2. Using the `stringLength` calculation from the previous question, what is the width of the image and what is the height in the following code?
`new GreenfootImage(stringLength, 16)`
3. True or false: to display text in Greenfoot, we draw it on an image.

### 8.3.2: Drawing Text

• Remember in lesson 6.2.3 we discussed how to draw shapes
• To draw shapes, we called drawing and filling methods of the GreenfootImage class
• One of the drawing methods is `drawString()`:

drawString(string, leftX, baselineY): draws the string on the image at the specified location.

• Where:
• string: the text to draw on the image
• leftX is the x-coordinate from the left edge of the image
• baselineY is y-coordinate of the baseline of the text
• We can see these locations in the following image:

• When we call `drawString()`, we are drawing text onto the image
`image.drawString(text + value, 1, 12);`
• In order to draw all the text, we must have an image that is of sufficient size
• When providing space, we must allow room for the entire height including the descent
• At the same time, we do not want an image that is overly large or we waste memory space
• Leading is usually referred to as line spacing in word processing software
• If we are drawing a single line of text then leading is optional
• Note that when we draw the string, it will be drawn in the current color and font setting of the image
• The following example shows how to create a transparent label
• The image must be set into an `Actor` subclass
• Images can only be displayed in `World` or `Actor` subclasses.

#### Creating a Label

```String text = "test string";
int width = text.length() * 10;
GreenfootImage label = new GreenfootImage(width, 16);
label.setColor(Color.WHITE); // text color
label.drawString(text, 1, 12);
setImage(label); // set image for this Actor
```

#### Simplified Text Images

• In version 2.0.1, Greenfoot introduced a new simplified way to create images with text
• We can now use the following constructor of the `GreenfootImage` class:

public GreenfootImage(string, fontSize, foregroundColor, backgroundColor)

• Where:
• string: the string to draw on the image
• fontSize: the approximate size of the font in pixels
• foregroundColor: the color of the text
• backgroundColor: the color of the background behind the text
• For example, we can construct a simple label like the above using:
```GreenfootImage label = new GreenfootImage("test string",
16, Color.WHITE, new Color(0, 0, 0, 0));
setImage(label);
```
• We must still set our image into an `Actor` subclass
• Note that we have no control over the placement of the text within the image

#### Check Yourself

1. The `GreenfootImage` method to draw a string on an image is ________.
2. True or false: a zero value for the y-coordinate of the `drawString()` method would hide most of the string.
3. True or false: leading is another term for line spacing.
4. Which letters of the alphabet have a descent?
5. True or false: to display a label (image with text) we must set the image into an `Actor` subclass.
6. True or false: Starting with Greenfoot 2.0.1, we can create an image with text drawn on it in one statement.

### 8.3.3: Text and Fonts

• A font is a set of text characters
• We can control the font of the text we write onto an image using class `Font`
• Class Font contains methods and constants for specifying fonts
• To use fonts, we must import the API class:
`import java.awt.Font;`
• The usual `Font` constructor takes three arguments:
• Where:
• fontName: name of the font like "Monospaced", "SansSerif", "Serif", etc.
• fontStyle: Font.PLAIN, Font.ITALIC or Font.BOLD
• fontSize: size of the font measured in points (1/72 of inch)
• For example:
```int FONT_SIZE = 24;
Font f = new Font("Serif",
Font.BOLD | Font.ITALIC,
FONT_SIZE);
```
• Java guarantees that at least three font names will be available:
• Monospaced
• SanSerif
• Serif
• Any other font depends on the users system
• Thus it is a good idea to use one of the guaranteed fonts in a scenario
• Notice how we can combine multiple font styles by separating each style with a vertical bar "`|`"
• The single vertical bar is known as the bitwise inclusive OR operator

#### Changing Fonts

• The `GreenfootImage` class has methods to set and get fonts as shown below
• Notice that the `Counter` class does not set a font
• Instead, it uses the default font of `GreenfootImage`
• However, the `ScoreBoard` class does get and set fonts in the `makeImage()` method
• Here is an excerpt from the `makeImage()` method:
```Font font = image.getFont();
font = font.deriveFont(FONT_SIZE);
image.setFont(font);
```
• Notice the calling of method `deriveFont()` in the above excerpt
• This is another way to create a font: derive one font from another
• The overloaded method `deriveFont()` creates a new font with a different style, size or other transformation

#### Some GreenfootImage Methods for Working with Fonts

Method Description
getFont() Returns the current font.
setFont(Font f) Set the current font for subsequent text operations.

#### Check Yourself

1. A font is a set of text ________.
2. Of the following, font ________ is not a parameter of the `Font` constructor.
1. font name
2. font style
3. font size
4. font character
3. Java guarantees three fonts will exist: ________, ________ and ________.
4. The three styles are available for a font are ________, ________ and ________.
5. True or false: the only way to create a `Font` object is to call a `Font` constructor.

### 8.3.4: Making a Text Area

• We have looked at how to make single lines of text
• Now we look at how to make text areas with multiple lines of text
• As an example, we look at the code for `makeImage()` in `Scoreboard`

#### Method `makeImage()` of `ScoreBoard`

```private void makeImage(String title, String prefix,
int score)
{
GreenfootImage image =
new GreenfootImage(WIDTH, HEIGHT);
image.setColor(new Color(255,255,255, 128));
image.fillRect(0, 0, WIDTH, HEIGHT);
image.setColor(new Color(0, 0, 0, 128));
image.fillRect(5, 5, WIDTH - 10, HEIGHT - 10);

Font font = image.getFont();
font = font.deriveFont(FONT_SIZE);
image.setFont(font);
image.setColor(Color.WHITE);
image.drawString(title, 60, 100);
image.drawString(prefix + score, 60, 200);
setImage(image);
}
```

#### Preparing the Image

• The first block of code prepares an image for drawing multiple lines of text
• The following two lines make a translucent background used for the border:
```image.setColor(new Color(255,255,255, 128));
image.fillRect(0, 0, WIDTH, HEIGHT);
```
• Then the next two lines hollow out the inner area by overlaying a black translucent background:
```image.setColor(new Color(0, 0, 0, 128));
image.fillRect(5, 5, WIDTH - 10, HEIGHT - 10);
```

#### Drawing the Text

• The next block of code draws the text onto the prepared image
• First the code creates and sets a new font:
```Font font = image.getFont();
font = font.deriveFont(FONT_SIZE);
image.setFont(font);
```
• Then the code sets the color of the text:
```image.setColor(Color.WHITE);
```
• After this the two lines of text are written onto the image:
```image.drawString(title, 60, 100);
image.drawString(prefix + score, 60, 200);
```
• Finally, the image is set for display:
```setImage(image);
```

#### Centering the Text Area

• The game adds the score board in the `gameOver()` method of `Space`
• The code makes sure that the score board is centered no matter the size of the scenario screen
• Can you see how the centering works?

#### Method `gameOver()` of `Space`

```public void gameOver()
{
int finalScore = scoreCounter.getValue();
ScoreBoard sb = new ScoreBoard(finalScore);
addObject(sb, getWidth() / 2, getHeight() / 2);
removeObjects(getObjects(Bullet.class));
}
```

#### Check Yourself

1. A text area contains ________ lines of text.
2. Of the following, ________ is NOT one of the steps for making a text area?
1. Prepare an image
2. Draw text on an image using `drawString()`
3. Set the image to be displayed in an `Actor`
4. Create a subclass of the `Text` class and calling its methods
3. True or false: The (x, y) coordinates of an `Actor` are located at the `Actor`'s center point when placed into a world

### 8.3.5: Restarting the Scenario

• When the Asteroids game ends, we want to let players restart the game
• To restart the game, we need to:
• Remove the Asteroids from the world
• Add a new set of asteroids to the world
• Add a new rocket to the world
• Set the score to 0
• Remove the score board from the world
• Most of the code belongs is in the `Space` class
• However, we need to start the process in the `ScoreBoard` class

#### Starting the Restart

• One way to restart the scenario is to have the user click the score board
• In essence, we turn the `ScoreBoard` class into a simple button
• To turn the class into a button we add code to the `act()` method as shown below

#### Method `act()` of `ScoreBoard`

```public void act()
{
if (Greenfoot.mousePressed(this))
{
Space space = (Space) getWorld();
space.restartGame();
space.removeObject(this);
}
}
```
• When the user clicks the score board, the method gets a reference to the `Space` world
• With a reference to the `Space` world, we can call the `restartGame()` method
• Method `restartGame()` is a new method that must be added to `Space`
• We can see the method below

#### Method `restartGame()` Added to `Space`

```public void restartGame()
{
List asteroids = getObjects(Asteroid.class);
removeObjects(asteroids);
addAsteroids(startAsteroids);
Rocket rocket = new Rocket();
addObject(rocket, getWidth()/2 + 100, getHeight()/2);
scoreCounter.add(-scoreCounter.getValue());
}
```
• The first two lines get a list of all the `Asteroid` objects and removes them from the world
• To remove the asteroids, the code calls the method:

getObjects(Class cls): Returns a list of all the objects in the world.

• We can restrict the type to a single class of actors like `Asteroid`
• With the list we can call another method:

removeObjects(Collection objects): Remove a list of objects from the world.

• Using these two methods in sequence makes it easy to remove all the objects of a single type from a world
• The next three lines create a new set of asteroids and add a rocket
• Next, the `scoreCounter` is set to zero

#### Check Yourself

1. To make a button out of an `Actor`, test for ________ in the `act()` method.
2. The two methods of the `World` class do we call in sequence to remove all the objects of a single type from a world are ________.
1. `findObjects()` and `deleteObjects()`
2. `gameOver()` and `stop()`
3. `getObjects()` and `deleteObjects()`
4. `getObjects()` and `removeObjects()`

### Exercise 8.3

In this exercise, we modify the `ScoreBoard` and add code to restart the game.

#### Specifications

1. Start Greenfoot and open your completed Asteroids scenario from Exercise 8.2.

If you do not have your completed exercise 8.2 scenario, then use scenario from the start of Exercise 8.2.

2. Compile and run the scenario to verify it works well before you start.

If you have problems, ask a classmate or the instructor for help as needed.

3. Open the `Space` class and add a statement to import the `List` library.
```import java.util.List;
```
4. Add the following method to the Space class:
```public void restartGame()
{
List asteroids = getObjects(Asteroid.class);
removeObjects(asteroids);
addAsteroids(startAsteroids);
Rocket rocket = new Rocket();
addObject(rocket, getWidth()/2 + 100, getHeight()/2);
scoreCounter.add(-scoreCounter.getValue());
}
```

Compile the class and verify there are no errors. Resolve any errors you find, getting help from a classmate or the instructor as needed.

5. In the `ScoreBoard` class, add the following `act()` method:
```public void act()
{
if (Greenfoot.mousePressed(this))
{
Space space = (Space) getWorld();
space.restartGame();
space.removeObject(this);
}
}
```
6. Compile and run your scenario to verify all the changes work well.

The scenario should restart when the player clicks the score board. If you have problems, ask a classmate or the instructor for help as needed.

7. Save a copy of your completed lesson scenario to upload to Blackboard as part of assignment 7.

As time permits, read the following sections and be prepared to answer the Check Yourself questions in the section: 8.3.6: Review.

### 8.3.6: Review

Answer these questions to check your understanding. You can find more information by following the links after the question.

1. True or false: to display text in Greenfoot, we draw it on an image. (8.3.1)
2. For the following code snippets, what is the value of `stringLength`? (8.3.1)
```scoreCounter = new Counter("Score: ");
...
public Counter(String prefix)
{
text = prefix;
stringLength = (text.length() + 2) * 10;
// rest of class omitted
```
3. Using the `stringLength` calculation from the previous question, what is the width of the image and what is the height in the following code? (8.3.1)
`new GreenfootImage(stringLength, 16)`
4. The `GreenfootImage` method to draw a string on an image is _______________. (8.3.2)
5. The optimal image size when drawing text on a transparent image is (8.3.2)
1. the size of the scenario's world.
2. large enough for the text and no larger.
3. WIDTH wide and HEIGHT high.
4. as large as need be to make the image look good.
6. Images can only be displayed in `World` or _____________ subclasses. (8.3.2)
7. True or false: a zero value for the y-coordinate of the `drawString()` method would hide most of the string. (8.3.2)
8. True or false: Starting with Greenfoot 2.0.1, we can create an image with text drawn on it in one statement. (8.3.2)
9. What is a font? (8.3.3)
10. Which of the following is not a parameter of the `Font` constructor?
1. font name
2. font style
3. font size
4. font alphabet
11. What three font names are guaranteed by Java to exist on all computer systems? (8.3.3)
12. What three styles are available for a font? (8.3.3)
13. True or false: the only way to create a `Font` object is to use a `Font` constructor. (8.3.3)
14. Which of the following is NOT one of the steps for making a multi-line text area? (8.3.4)
1. Prepare an image
2. Draw text on an image using `drawString()`
3. Set the image to be displayed in an `Actor`
4. Create a subclass of the `Text` class and calling its methods
15. True or false: The (x, y) coordinates of an `Actor` are located at the `Actor`'s center point. (8.3.4)
16. To make a button out of an `Actor`, test for _____________ in the `act()` method. (8.3.5)
17. What two methods do we call in sequence to remove all the objects of a single type from a world? (8.3.5)

## Wrap Up

Due Next:
A6: Visualizer Redux (3/28/12)
A7: Asteroids Deluxe (4/4/12)
• When class is over, please shut down your computer
• You may complete unfinished lesson exercises at any time before the due date.

Show Navigation

Last Updated: May 05 2012 @19:40:34