What We Will Cover
Illuminations
Questions from last class or the Reading?
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:
- Completeness
- Person has not shared before
- Extra features or creativity
^ top
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
|
^ top
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
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
- The invisible rectangle around an image used to detect collisions in known as a ________ box.
- True or false: a subclass of
Actor, like Bug, can also be called an Actor because Actor is the superclass type.
- What are the core game mechanics of the Asteroids game?
- Ship navigation
- Shooting things
More Information
^ top
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
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
- The default background color of a Greenfoot world is ________.
- The purpose of the following lines of code from
createStars() is to generate ________ coordinates.
int x = Greenfoot.getRandomNumber(getWidth());
int y = Greenfoot.getRandomNumber(getHeight());
- 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));
More Information
^ top
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
- When the user presses the left or right arrow keys, the rocket rotates ________ degrees.
- When the user presses the up-arrow key, the value ________ is passed to the
ignite() method in the following code:
ignite(Greenfoot.isKeyDown("up"));
- 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.
- 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));
- Describe the screen wrap-around concept in English.
^ top
8.1.4: Firing Bullets
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
- The three parts to controlling timing over multiple scenario cycles are:
- ________
- ________
- ________
- Find the lines numbers of each of the three steps for controlling timing in the
Bullet class.
^ top
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
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
- True or false: When using bounding boxes, transparent pixels are ignored during collision detection.
- True or false: Bounding boxes are always accurate because all images are rectangles.
- True or false: To improve the accuracy of bounding box collision detection, leave transparent pixels around the image.
- True or false: Checking a bounding box for collisions is a relatively fast computation.
^ top
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()
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
- True or false:
getWorld() returns the current subclass of world as a World object.
- True or false: casting permanently changes one type of object into another type
- The following code causes a compiler error:
Space world = getWorld();
Enter the code to correct the problem.
^ top
Exercise 8.1
In this exercise, we implement collision detection for the rocket in Asteroids. Also, we implement a simple scoring system.
Specifications
- Download the following file and save it in the scenario folder of Greenfoot.
Scenario file: asteroids-2b.zip.
- Start Greenfoot and open the scenario.
Greenfoot unzips the file for us.
- 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
}
}
- 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.
- 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.
- 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.
- Add a method to the
Space class to add to the score, like:
public void addToScore(int amount)
{
scoreCounter.add(amount);
}
- 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?
- 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.
- 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.
^ top
8.1.7: Review
Answer these questions to check your understanding. You can find more information by following the links after the question.
- Asteroids is a video arcade game released in _______. (8.1.1)
- What are core game mechanics of Asteroids? (8.1.1)
- What is the default background color of a Greenfoot world? (8.1.2)
- 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());
- What does the following lines of code do? (8.1.2)
int color = 120 - Greenfoot.getRandomNumber(100);
background.setColor(new Color(color,color,color));
- Which of the following statements rotates the actor 5 degrees? (8.1.3)
setRotation(getRotation() + 5);
setRotation(5);
setRotation(getRotation(), 5);
setRotation(getRotation() = 5);
- Fact or fiction: a boolean parameter accepts one of two values, either
true or false. (8.1.3)
- 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)
- Which of the following is NOT part of controlling timing of multiple scenario cycles? (8.1.4)
- Declare a counter instance variable.
- Change the value of the counter in the
act() method.
- Use an
if-statement to test if the counter has reached its goal.
- Call the
move() method of the actor.
- What is a bounding box? (8.1.5)
- A collision between two objects.
- The box that is drawn around an object.
- A highlighted area of an image.
- An imaginary box around an image.
- True or false: When using bounding boxes, transparent pixels are ignored during collision detection. (8.1.5)
- True or false: Bounding boxes are always accurate because all images are rectangles. (8.1.5)
- True or false: To improve the accuracy of bounding box collision detection, leave transparent pixels around the image. (8.1.5)
- True or false: Checking a bounding box for collisions is a relatively fast computation. (8.1.5)
- 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;
- Altering
- Casting
- Changing
- Redefining
- True or false:
getWorld() returns the current subclass of world as a World object. (8.1.6)
- True or false: casting changes one type of object into another type (8.1.6)
^ top
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
|
^ top
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
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
- The second weapon for the rocket in Asteroids is a ______ ______.
- True or false: the proton wave grows, destroying all asteroids in its path.
- The three key methods of the
ProtonWave class are ________, ________ and ________.
^ top
8.2.2: Initializing Images
Array of Images for the Proton Wave

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
Use of static Keyword
Check Yourself
- Storing data so that future requests for the data can be served faster is known as ________.
- True or false: the
images array variable stores all the image data.
- True or false: the array of
GreenfootImages referred to by the images variable stores all the image data.
- True or false: each image of the proton wave is stored at various locations in main computer memory.
- The
GreenfootImage method that changes the size of an image is ________.
- True or false: static member variables can be accessed by static methods
- True or false: instance member variables can be accessed by static methods
^ top
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?
- Add an instance variable to hold counting values
private int imageCount = 0;
- Change the value of the count from the
act() method
imageCount++;
- Use an
if-statement to test if the count has reached its goal
if (imageCount >= NUMBER_IMAGES)
- 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
- The three parts to controlling timing over multiple scenario cycles are:
- ________
- ________
- ________
- The array images are arranged from ________ to ________.
- How often is the
grow() method called? answer
^ top
8.2.4: Interacting with Objects in Range
Detecting Asteroids in Range
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
- True or false: The method
getObjectsInRange() returns a list of actors within a specified range
- 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.
- 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.
^ top
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?
The code has three parts and is used for controlling timing over multiple scenario cycles.
- 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
- What are the three parts to controlling the reload delay over multiple scenario cycles?
- Add an instance variable to hold counting values
private int protonDelayCount;
- Change the value of the count from the
act() method
protonDelayCount++;
- Use an
if-statement to test if the count has reached its goal
if (protonDelayCount >= protonReloadTime)
- True or false: the
protonReloadTime variable sets the number of act() cycles before the proton wave can fire again.
- 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.
^ top
Exercise 8.2
In this exercise, we add a proton wave recharge indicator.
Specifications
- Download the following file and save it in the scenario folder of Greenfoot.
Scenario file: asteroids-3.zip.
- Start Greenfoot and open the scenario.
Greenfoot unzips the file for us.
- Create a new actor named
StatusBar without an image.
For more information see lesson: 1.4.6: Creating an Actor.
- Add an import statement to the new class to import the
Color library.
import java.awt.Color;
- Add a constant for the default color.
private static final Color DEFAULT_COLOR =
new Color(0, 255, 0, 128);
- 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;
- 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);
}
- 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();
}
- 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();
}
}
- 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();
}
}
- Add two get-methods for accessing the width and height:
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
- 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.
- Open the
Rocket class and add an import statement to import the Color library.
import java.awt.Color;
- In the
Rocket class and add the following variable:
private StatusBar protonLoad;
- 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);
}
- Finally, in the
Rocket class, add the following statement to the act() method:
protonLoad.setPercentageFilled(protonDelayCount / 5);
- 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.
- 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.
^ top
8.2.6: Review
Answer these questions to check your understanding. You can find more information by following the links after the question.
- Storing data so that future requests for the data can be served faster is known as __________. (8.2.2)
- True or false: a GreenfootImage
array stores all the image data in the reference variable. (8.2.2)
- True or false: the array of
GreenfootImages referred to by the reference variable stores all the image data. (8.2.2)
- True or false: each image of an array of images is stored at various locations in main computer memory. (8.2.2)
- The
GreenfootImage method that changes the size of an image is __________. (8.2.2)
- True or false: static member variables can be accessed by static methods. (8.2.2)
- True or false: instance member variables can be accessed by static methods. (8.2.2)
- The three parts to controlling timing over multiple scenario cycles are: (8.2.3)
- _____________________________
- _____________________________
- _____________________________
- True or false: The method
getObjectsInRange() returns a list of actors within a specified range. (8.2.4)
- 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);
}
- 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)
^ top
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
|
^ top
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
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
Method updateImage() Displays Text
private void updateImage()
{
GreenfootImage image = getImage();
image.clear();
image.drawString(text + value, 1, 12);
}
Check Yourself
- 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
- 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)
- True or false: to display text in Greenfoot, we draw it on an image.
^ top
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
- The
GreenfootImage method to draw a string on an image is ________.
- True or false: a zero value for the y-coordinate of the
drawString() method would hide most of the string.
- True or false: leading is another term for line spacing.
- Which letters of the alphabet have a descent?
- True or false: to display a label (image with text) we must set the image into an
Actor subclass.
- True or false: Starting with Greenfoot 2.0.1, we can create an image with text drawn on it in one statement.
^ top
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:
Font(fontName, fontStyle, fontSize)
- 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
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
- A font is a set of text ________.
- Of the following, font ________ is not a parameter of the
Font constructor.
- font name
- font style
- font size
- font character
- Java guarantees three fonts will exist: ________, ________ and ________.
- The three styles are available for a font are ________, ________ and ________.
- True or false: the only way to create a
Font object is to call a Font constructor.
More Information
^ top
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
- A text area contains ________ lines of text.
- Of the following, ________ is NOT one of the steps for making a text area?
- Prepare an image
- Draw text on an image using
drawString()
- Set the image to be displayed in an
Actor
- Create a subclass of the
Text class and calling its methods
- True or false: The (x, y) coordinates of an
Actor are located at the Actor's center point when placed into a world
^ top
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());
}
Check Yourself
- To make a button out of an
Actor, test for ________ in the act() method.
- 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 ________.
findObjects() and deleteObjects()
gameOver() and stop()
getObjects() and deleteObjects()
getObjects() and removeObjects()
^ top
Exercise 8.3
In this exercise, we modify the ScoreBoard and add code to restart the game.
Specifications
- 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.
- 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.
- Open the
Space class and add a statement to import the List library.
import java.util.List;
- 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.
- 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);
}
}
- 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.
- 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.
^ top
8.3.6: Review
Answer these questions to check your understanding. You can find more information by following the links after the question.
- True or false: to display text in Greenfoot, we draw it on an image. (8.3.1)
- 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
- 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)
- The
GreenfootImage method to draw a string on an image is _______________. (8.3.2)
- The optimal image size when drawing text on a transparent image is (8.3.2)
- the size of the scenario's world.
- large enough for the text and no larger.
- WIDTH wide and HEIGHT high.
- as large as need be to make the image look good.
- Images can only be displayed in
World or _____________ subclasses. (8.3.2)
- True or false: a zero value for the y-coordinate of the
drawString() method would hide most of the string. (8.3.2)
- 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)
- What is a font? (8.3.3)
- Which of the following is not a parameter of the
Font constructor?
- font name
- font style
- font size
- font alphabet
- What three font names are guaranteed by Java to exist on all computer systems? (8.3.3)
- What three styles are available for a font? (8.3.3)
- True or false: the only way to create a
Font object is to use a Font constructor. (8.3.3)
- Which of the following is NOT one of the steps for making a multi-line text area? (8.3.4)
- Prepare an image
- Draw text on an image using
drawString()
- Set the image to be displayed in an
Actor
- Create a subclass of the
Text class and calling its methods
- True or false: The (x, y) coordinates of an
Actor are located at the Actor's center point. (8.3.4)
- To make a button out of an
Actor, test for _____________ in the act() method. (8.3.5)
- What two methods do we call in sequence to remove all the objects of a single type from a world? (8.3.5)
^ top
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.
^ top
Show Navigation
Last Updated: May 05 2012 @19:40:34
|