Chapter 7- Collisionsx

advertisement
Chapter 7 - Collision Detection:
Asteroids
Bruce Chittenden
7.1 Investigation: What is There?
When experimenting with the current scenario, you will
notice that some fundamental functionality is missing.
 The rocket does not move. It cannot be turned, nor can it
be moved forward.
 Nothing happens when an asteroid collides with the
rocket. It flies straight through it, instead of damaging the
rocket.
 As a result of this, you cannot lose. The game never ends,
and a final score is never displayed.
 The ScoreBoard, Explosion, and ProtonWave classes,
which we can see in the class diagram, do not seem to
feature in the scenario.
Exercise 7.1
Exercise 7.2
Controls for the Rocket
Collision Logic
Explosion Logic
ScoreBoard Logic
Implement ProtonWave
Exercise 7.3
Spacebar is used to fire a bullet
Exercise 7.4
Creates the Explosion Visual and
makes and Explosion Sound
Exercise 7.5
The visual is Present, But
It Does Not Do Anything
7.2 Painting Stars
The Asteroid Scenario does not use an image
file for the background. A world that does not
have a background image assigned will, by
default, get an automatically created
background image that is filled with plain white.
Exercise 7.6
The Background is Created
by These Three Statements
Exercise 7.7
Code to Create the Background
is Commented Out
Exercise 7.7
Exercise 7.8
Draw Oval
Draw Rectangle
Fill Oval
Exercise 7.8
Exercise 7.8
Exercise 7.8
Exercise 7.9
Exercise 7.9
13 Defined Constant Fields
Exercise 7.10
/*
* Method to create stars. The integer number is how many.
*/
private void createStars(int number)
{
// Need to add code here
}
Exercise 7.11
/*
* Method to create stars. The integer number is how many.
*/
Exercise 7.11
Create 300 Stars
Exercise 7.13
Clean Compile
Exercise 7.14
/**
* Add a given number of asteroids to our world. Asteroids are only added into
* the left half of the world.
*/
The addAsteroids creates a count number
private void addAsteroids(int count)
of asteroids and adds them to the World
{
for(int i = 0; i < count; i++)
{
int x = Greenfoot.getRandomNumber(getWidth()/2);
int y = Greenfoot.getRandomNumber(getHeight()/2);
addObject(new Asteroid(), x, y);
}
}
Exercise 7.15
Initialization
Loop-Condition
Increment
for (int i = 0; i < count; i++)
{
}
Exercise 7.16
/*
* Add a given number of asteroids to our world. Asteroids are only added into
* the left half of the world.
*/
private void addAsteroids(int count)
{
int I = o;
while ( i < count)
{
int x = Greenfoot.getRandomNumber(getWidth()/2);
int y = Greenfoot.getRandomNumber(getHeight()/2);
addObject(new Asteroid(), x, y);
i++;
}
}
Exercise 7.17
/*
* Add a given number of asteroids to our world. Asteroids are only added into
* the left half of the world.
*/
private void addAsteroids(int count)
{
for ( int i = 0; i < count; i++)
{
int x = Greenfoot.getRandomNumber(getWidth()/2);
int y = Greenfoot.getRandomNumber(getHeight()/2);
addObject(new Asteroid(), x, y);
}
}
Exercise 7.18
/*
* Method to create stars. The integer number is how many.
*/
Generate a random number for x and y
private void createStars(int number)
and place a star at that location
{
GreenfootImage background = getBackground();
for (int i = 0; i < number; i++)
{
int x = Greenfoot.getRandomNumber ( getWidth() );
int y = Greenfoot.getRandomNumber ( getHeight() );
background.setColor (new Color(255, 255, 255));
background.fillOval (x, y, 2, 2);
}
}
Exercise 7.18
Exercise 7.19
/*
* Method to create stars. The integer number is how many.
*/
Generate a random number
private void createStars(int number)
for color in the range 0 to 255
{
GreenfootImage background = getBackground();
for (int i = 0; i < number; i++)
{
int x = Greenfoot.getRandomNumber( getWidth() );
int y = Greenfoot.getRandomNumber( getHeight() );
int color = Greenfoot.getRandomNumber (256);
background.setColor(new Color(color, color, color));
background.fillOval(x, y, 2, 2);
}
}
Exercise 7.19
7.3 Turning
We want to make the rocket turn left or right
using the left and right arrow keys.
Exercise 7.20
/*
* Check whether there are any key pressed and react to them.
*/
private void checkKeys()
The method checkKeys
{
handles keyboard input
if (Greenfoot.isKeyDown("space"))
{
fire();
}
}
Exercise 7.21
Exercise 7.21
if (Greenfoot.isKeyDown("left"))
setRotation(getRotation() - 5);
if (Greenfoot.isKeyDown("right"))
setRotation(getRotation() + 5);
Left Negative Degrees
Right Positive Degrees
Exercise 7.21
/*
* Check whether there are any key pressed and react to them.
*/
private void checkKeys()
If left arrow key is down
{
rotate left 5 degrees
if (Greenfoot.isKeyDown ("space"))
{
fire();
}
if (Greenfoot.isKeyDown ("left"))
setRotation (getRotation() - 5);
}
Exercise 7.21
Exercise 7.22
/*
* Check whether there are any key pressed and react to them.
*/
private void checkKeys()
If right arrow key is down
{
rotate right 5 degrees
if (Greenfoot.isKeyDown("space"))
{
fire();
}
if (Greenfoot.isKeyDown ("left"))
setRotation (getRotation() - 5);
if (Greenfoot.isKeyDown ("right"))
setRotation (getRotation() + 5);
}
Exercise 7.22
7.4 Flying Forward
Our Rocket class is a subclass of the
SmoothMover class. This means that it holds a
movement vector that determines its movement
and that it has a move () method that makes it
move according to this vector
Exercise 7.23
/*
* Do what a rocket's gotta do. (Which is: mostly flying about, and turning,
* accelerating and shooting when the right keys are pressed.)
*/
public void act()
Add the move () method
{
move ();
checkKeys();
reloadDelayCount++;
}
Exercise 7.23
The rocket does not move
because our move vector has not
been initialized and contains zero
Exercise 7.24
/*
Add an initial movement to
* Initialize this rocket.
the rocket constructor.
*/
public Rocket()
{
reloadDelayCount = 5;
addForce ( new Vector (13, 0.3)); //initially slow drifting
}
Exercise 7.24
The rocket drifts slowly toward
the right of the World
Exercise 7.25
/*
* Check whether there are any key pressed and react to them.
*/
private void checkKeys()
Add a call to ignite
{
if (Greenfoot.isKeyDown("space"))
fire();
ignite (Greenfoot.isKeyDown ("up"));
if (Greenfoot.isKeyDown("left"))
setRotation(getRotation() - 5);
if (Greenfoot.isKeyDown("right"))
setRotation(getRotation() + 5
}
Exercise 7.26
Define a stub method for ignite
/*
* Go with thrust on
*/
private void ignite (boolean boosterOn)
{
}
Exercise 7.27
Pseudo Code
when “up” arrow key is pressed
change image to show engine fire;
add movement;
when “up” arrow key is released
change back to normal image;
Exercise 7.27
The ignite method complete
/*
* Go with thrust on
*/
private void ignite (boolean boosterOn)
{
if (boosterOn)
{
setImage (rocketWithThrust);
addForce (new Vector (getRotation(), 0.3));
}
else
{
setImage (rocket);
}
}
Exercise 7.27
7.5 Colliding with Asteroids
Pseudo Code
If (we have collided with an asteroid)
{
remove the rocket from the world;
place an explosion into the world;
show final score (game over);
}
Exercise 7.28
/*
* Check for a collision with an Asteroid
*/
private void checkCollision()
{
}
Define a stub method for
checkCollision
Exercise 7.29
/*
* Do what a rocket's gotta do. (Which is: mostly flying about, and turning,
* accelerating and shooting when the right keys are pressed.)
*/
Public void act()
Make a call to checkCollision
{
from the Rocket Act method
move ();
checkKeys();
checkCollision();
reloadDelayCount++;
}
Intersecting Objects
List getIntersectingObjects (Class cls)
Actor getOneIntersectingObject (Class cls)
Bounding Box
Visible Image
Exercise 7.30
/*
* Check for a collision with an Asteroid
*/
private void checkCollision()
{
Actor a = getOneIntersectingObject (Asteroid.class);
if (a != null)
{
}
}
Check for intersecting
with an Asteroid
Exercise 7.31
Add the code to remove the
Rocket from the World and
display an Explosion
/*
* Check for a collision with an Asteroid
*/
private void checkCollision()
{
Actor a = getOneIntersectingObject (Asteroid.class);
if (a != null)
{
Space space = (Space) getWorld();
space.addObject (new Explosion(), getX(), getY());
space.removeObject (this);
space.gameOver();
}
}
Exercise 7.31
Exercise 7.32
The problem with this code is that
we removed the Object and then
attempted to use it’s coordinates
/*
* Check for a collision with an Asteroid
*/
private void checkCollision()
{
Actor a = getOneIntersectingObject (Asteroid.class);
if (a != null)
{
World world = getWorld();
world.removeObject (this);
world.addObject (new Explosion(), getX(), getY());
}
}
Exercise 7.32
Exercise 7.33
This is a very advanced exercise. A more sophisticated way to show an
explosion is introduced in the Greenfoot tutorial videos.
Making Explosions, Part I (length: ~5 min, 11.6 MB)
Making Explosions, Part II (length: ~18.5 min, 57.9 MB)
Making Explosions, Part III (length: ~15 min, 52.2 MB)
7.6 Casting
In computer science, type conversion, typecasting, and
coercion refers to different ways of, implicitly or explicitly,
changing an entity of one data type into another. This is
done to take advantage of certain features of type
hierarchies or type representations. One example would be
small integers, which can be stored in a compact format
and converted to a larger representation when used in
arithmetic computations. In object-oriented programming,
type conversion allows programs to treat objects of one
type as one of their ancestor types to simplify interacting
with them.
Exercise 7.34
Right Click on ScoreBoard then click on
new ScoreBoard and drag it into the World
Exercise 7.35
/*
* Create a score board with dummy result for testing.
*/
public ScoreBoard()
{
this(100);
The ScoreBoard class
}
has two constructors, a
Default Constructor
/*
and the actual
* Create a score board for the final result.
Constructor
*/
public ScoreBoard(int score)
{
makeImage("Game Over", "Score: ", score);
}
http://www.jafar.com/java/csel/index.html
Exercise 7.36
/*
* Make the score board image.
*/
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(255, 0, 0, 128));
image.fillRect(5, 5, WIDTH-10, HEIGHT-10);
Font font = image.getFont();
font = font.deriveFont(ITALIC, FONT_SIZE);
image.setFont(font);
image.setColor(Color.BLUE);
image.drawString(title, 60, 100);
image.drawString(prefix + score, 60, 200);
setImage(image);
}
Exercise 7.36
The information about Fonts
is in java.awt.Font
import greenfoot.*; // (World, Actor, GreenfootImage, and Greenfoot)
import java.awt.Color;
import java.awt.Font;
import java.util.Calendar;
public class ScoreBoard extends Actor
{
public static final float FONT_SIZE = 48.0f;
public static final int ITALIC = 2;
public static final int WIDTH = 400;
public static final int HEIGHT = 300;
Example 7.36
Exercise 7.37
gameOver Method is a stub
Exercise 7.38
/*
* This method is called when the game is over to display the final score.
*/
public void gameOver()
{
addObject(new ScoreBoard(999), getWidth()/2, getHeight()/2);
}
Exercise 7.39
Exercise 7.40
To insure that the Score is placed
in the of the World, center it at
½ width and ½ height
/*
* This method is called when the game is over to display the final score.
*/
public void gameOver()
{
addObject(new ScoreBoard(999), getWidth()/2, getHeight()/2);
}
Exercise 7.41
The error comes from the fact that gameOver () is defined in class Space while
getWorld returns a type of World. To solve this problem we tell the compiler
explicitly that this world we are getting is actually of type Space.
Space space = (space) getWorld();
Code 7.1
/*
* Check for a collision with an Asteroid
*/
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();
}
}
Exercise 7.42
/*
Cast (Space)
* Check for a collision with an Asteroid
*/
private void checkCollision()
{
Actor a = getOneIntersectingObject (Asteroid.class);
if (a != null)
{
Space space = (Space) getWorld();
space.addObject (new Explosion(), getX(), getY());
space.removeObject (this);
space.gameOver();
}
}
Exercise 7.43
inconvertible types
7.7 Adding Fire Power: The Proton Wave
The idea is this: Our proton wave, once
released, radiates outward from our rocket ship,
damaging or destroying every asteroid in its
path. Since it works in all directions
simultaneously, it is a much more powerful
weapon than our bullets.
Exercise 7.44
Does not Move
Does not Disappear
Does not cause Damage
Exercise 7.45
public ProtonWave()
{
initializeImages();
}
The Constructor and Two Methods
ProtonWave ()
initializeImages ()
act ()
public static void initializeImages()
{
if(images == null)
{
GreenfootImage baseImage = new GreenfootImage("wave.png");
images = new GreenfootImage[NUMBER_IMAGES];
int i = 0;
while (i < NUMBER_IMAGES)
{
int size = (i+1) * ( baseImage.getWidth() / NUMBER_IMAGES );
images[i] = new GreenfootImage(baseImage);
images[i].scale(size, size);
i++;
}
}
}
public void act()
{
}
Exercise 7.46
/*
* Create a new proton wave.
*/
public ProtonWave()
{
}
/*
* Create the images for expanding the wave.
*/
public static void initializeImages()
{
}
/*
* Act for the proton wave is: grow and check whether we hit anything.
*/
public void act()
{
}
Exercise 7.47
/*
* Create the images for expanding the wave.
*/
public static void initializeImages()
{
if(images == null)
{
GreenfootImage baseImage = new GreenfootImage("wave.png");
images = new GreenfootImage[NUMBER_IMAGES];
int i = 0;
while (i < NUMBER_IMAGES)
{
int size = (i+1) * ( baseImage.getWidth() / NUMBER_IMAGES );
images[i] = new GreenfootImage(baseImage);
images[i].scale(size, size);
i++;
}
}
}
Exercise 7.47
Pseudo Code
If we have not created the images do so now
get the base Image from the file wave.png
create an array of NUMBER_IMAGES (30) images
while there are still images to initialize
calculate the size of this image to be the width of the image divided by the
number of images multiplied by the index of this image
set the next element in the array of images to the base image scaled to new size
7.8 Growing the Wave
GreenfootImage [ ]
0 1 2 3 4
29
Code 7.2
/*
* Create the images for expanding the wave.
*/
public static void initializeImages()
{
if (images == null)
{
GreenfootImage baseImage = new GreenfootImage("wave.png");
images = new GreenfootImage[NUMBER_IMAGES];
int i = 0;
while (i < NUMBER_IMAGES)
{
int size = (i+1) * ( baseImage.getWidth() / NUMBER_IMAGES );
images[i] = new GreenfootImage(baseImage);
images[i].scale(size, size);
i++;
}
}
}
Exercise 7.48
/*
* Create the images for expanding the wave.
*/
public static void initializeImages()
for Loop
{
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);
}
}
}
Exercise 7.49
/*
* Create a new proton wave.
*/
public ProtonWave()
{
initializeImages();
setImage(images [0]);
}
Exercise 7.49
Exercise 7.50
/*
* Current size of the wave.
*/
private int imageCount = 0;
Exercise 7.51
/*
* Grow the wave. If we get to full size remove it.
*/
private void grow ()
{
}
Exercise 7.52
/*
* Act for the proton wave is: grow and check whether we hit anything.
*/
public void act()
{
grow();
}
/*
* Grow the wave. If we get to full size remove it.
*/
private void grow ()
{
}
Exercise 7.53
Pseudo Code
if (our index has exceed the number of images)
remove the wave from the world;
else
set the next image in the array and
increment the index;
Exercise 7.53
/*
* Grow the wave. If we get to full size remove it.
*/
private void grow ()
{
if (imageCount >= NUMBER_IMAGES)
getWorld().removeObject (this);
else
setImage(images[imageCount++]);
}
Exercise 7.54
Right Click on ProtonWave
Click on new ProtonWave()
Drag into the World
Click >Act to watch
the wave grow
Exercise 7.55
/*
* Create a new proton wave.
*/
public ProtonWave()
{
initializeImages();
setImage(images [0]);
Greenfoot.playSound ("proton.wav");
}
Add Sound
Exercise7.56
/*
* Release a proton wave (if it is loaded).
*/
private void startProtonWave()
{
}
Does Not Need Any
Parameters and Does Not
Return Anything
Exercise 7.57
/*
* Release a proton wave (if it is loaded).
*/
private void startProtonWave()
{
ProtonWave wave = new ProtonWave();
getWorld().addObject (wave, getX(), getY());
}
Exercise 7.58
/*
* Check whether there are any key pressed and react to them.
*/
private void checkKeys()
{
if (Greenfoot.isKeyDown("space"))
fire();
if (Greenfoot.isKeyDown("z"))
startProtonWave();
ignite (Greenfoot.isKeyDown ("up"));
if (Greenfoot.isKeyDown("left"))
setRotation(getRotation() - 5);
if (Greenfoot.isKeyDown("right"))
setRotation(getRotation() + 5);
}
Exercise 7.58
Exercise 7.59
Proton Wave Can Be
Released Too Fast by
Holding Down the z Key
Exercise 7.59
private static final int gunReloadTime = 5;
// The minimum delay between firing the gun.
private static final int protonReloadTime = 100; // The minimum delay between proton wave bursts.
private int reloadDelayCount;
private int protonDelayCount;
// How long ago we fired the gun the last time.
// How long ago we fired the proton wave the last time.
private GreenfootImage rocket = new GreenfootImage("rocket.png");
private GreenfootImage rocketWithThrust = new GreenfootImage("rocketWithThrust.png");
A Delay Count of 100 Seems
More Reasonable Than 500
Exercise 7.59
/*
* Do what a rocket's gotta do. (Which is: mostly flying about, and turning,
* accelerating and shooting when the right keys are pressed.)
*/
public void act()
{
move ();
checkKeys();
checkCollision();
reloadDelayCount++;
protonDelayCount++;
}
Exercise 7.59
/*
* Release a proton wave (if it is loaded).
*/
private void startProtonWave()
{
if (protonDelayCount >= protonReloadTime)
{
ProtonWave wave = new ProtonWave();
getWorld().addObject (wave, getX(), getY());
protonDelayCount = 0;
}
}
Exercise 7.59
7.9 Interacting with Objects in Range
List getObjectsInRange (int radius, Class cls)
Exercise 7.60
/*
* Act for the proton wave is: grow and check whether we hit anything.
*/
public void act()
{
checkCollision();
grow();
}
/*
* Explode all intersecting asteroids.
*/
private void checkCollision()
{
}
Exercise 7.61
/*
* Act for the proton wave is: grow and check whether we hit anything.
*/
public void act()
{
checkCollision();
grow();
}
/*
* Explode all intersecting asteroids.
*/
private void checkCollision()
{
}
Exercise 7.62
/*
* Explode all intersecting asteroids.
*/
private void checkCollision()
{
int range = getImage().getWidth() / 2;
}
Exercise 7.63
import greenfoot.*; // (World, Actor, GreenfootImage, and Greenfoot)
import java.util.List;
/*
* Explode all intersecting asteroids.
*/
private void checkCollision()
{
int range = getImage().getWidth() / 2;
List<Asteroid> asteroids = getObjectsInRange (range, Asteroid.class);
}
Exercise 7.64
Parameter is the
Level of Damage
/*
* Hit this asteroid dealing the given amount of damage.
*/
public void hit(int damage)
{
stability = stability - damage;
if(stability <= 0)
breakUp ();
}
Before We Make Changes to the
Method checkCollision (),
Lets Look at the Hit Method
Exercise 7.65
/* The damage this wave will deal */
private static final int DAMAGE = 30;
Exercise 7.66
/*
* Explode all intersecting asteroids.
*/
private void checkCollision()
{
int range = getImage().getWidth() / 2;
List<Asteroid> asteroids = getObjectsInRange (range, Asteroid.class);
for (Asteroid a : asteroids)
a.hit (DAMAGE);
}
7.10 Further Development
The following are some suggestions for future
work, in the form of exercises. Many of them
are independent of each other – they do not
need to be done in this particular order. Pick
those first that interest you most, and come
up with some extension of your own.
Exercise 7.67
/*
* Keep track of the score.
*/
public void countScore (int count)
{
scoreCounter.add(count);
}
Exercise 7.67
private void breakUp()
{
Space space = (Space) getWorld();
Greenfoot.playSound("Explosion.wav");
if(size <= 16) // Removing an Asteroid is worth 25 points
{
getWorld().removeObject(this);
space.countScore(25);
}
else
// Splitting an Asteroid is worth 10 points
{
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);
space.countScore(10);
}
}
Exercise 7.67
7.11 Summary of Programming Techniques
Understanding lists and loops is initially quite
difficult, but very important in programming, so
you should carefully review these aspects of your
code if you are not yet comfortable in using them.
The more practice you get, the easier it becomes.
After using them for a while, you will be surprised
that you found them so difficult at first.
7.11 Summary of Programming Techniques
Download