Colliding with asteroids
7.5 Colliding with asteroids
The most obvious fault with our asteroids game at this stage is that we can fly right through the asteroids. That leaves not much of a challenge to play this game, since we cannot lose. We shall fix that now.
The idea is that our rocket ship should explode when we crash into an asteroid. If you did the exercises earlier in this chapter, then you have already seen that we have a fully functional Explosion class available in our project. Simply placing an explosion into the world will show an adequate explosion effect.
Thus, a rough description of the task to solve is this:
if (we have collided with an asteroid) {
remove the rocket from the world; place an explosion into the world; show final score (game over);
Before we look into solving these subtasks, we prepare our source code to implement this task, as we did before with other functionality. We follow the same strategy as before: Since this is a separate subtask, we shall put it into a separate method, in order to keep our code well structured and easily readable. You should usually start the implementation of new functionality like this. The next exercise achieves this.
Exercise 7.28 Create a new method stub (a method with an empty body) in class Rocket for checking for collisions with asteroids. Call it checkCollision. This method can be private and needs no return value and no parameters.
Concept: Exercise 7.29 In the Rocket’s act method, add a call to the checkCollision
Greenfoot
method. Ensure that your class compiles and runs again.
provides several methods for
collision detec-
tion. They are in
The first subtask is to check whether we have collided with an asteroid. Greenfoot’s Actor class
the Actor class.
contains a number of different methods to check for collisions with different functionality.
| ■ Chapter 7 Collision detection: Asteroids
Appendix C presents a summary of the different collision detection methods and their function- ality. This might be a good time to have a quick look through it. At some stage, you should become familiar with all the collision detection methods.
For our purpose getIntersectingObjects seems like a good fit. Two objects intersect if any of the pixels in their images intersect. This is pretty much what we need.
There is one small problem: transparent pixels in the actor images.
Concept:
Images in Greenfoot are always rectangles. When we see non-rectangular images, such as the rocket, this is because some pixels in the image are transparent (invisible; they contain no
The bounding box
color). For the purpose of our program, however, they are still part of the image.
of an image is the enclosing rectangle
Figure 7.2 shows the rocket and asteroid images with their bounding boxes. The bounding box is
of that image.
the edge of the actual image. (The image of the rocket is a little bigger than what seems neces- sary to make it the same size as the second rocket image, rocketWithThrust, which shows the flame in the currently empty area.)
In Figure 7.2, the images intersect, even though their visible parts do not touch. The collision detection methods will report this as an intersection. They work with the bounding boxes, and pay no attention to the non-transparent parts of the image.
As a result, our rocket will make “contact” with an asteroid even though, on screen, there seems to be still a little distance between them.
For our asteroids game, we choose to ignore this. Firstly, the distance is small, so often players will not notice. Secondly, it is easy enough to come up with a story line to explain this effect (“flying too close to an asteroid destroys your ship because of the gravitational pull”).
Sometimes it would be nice to check whether the actual visible (non-transparent) parts of an image intersect. This is possible, but much more difficult. We will not discuss this here.
Now that we have decided to go with intersection, we can look at the Actor methods again. There are two methods for checking object intersection. Their signatures are
List getIntersectingObjects(Class cls) Actor getOneIntersectingObject(Class cls)
Both methods accept a parameter of type Class (which means that we can check for intersec- tions with a specific class of object if we want to). The difference is that one method will return
a list of all objects that we currently intersect with, while the other returns only a single object. In case we intersect more than one other object, the second method randomly chooses one of them and returns it.
bounding box
Figure 7.2 Two actor images and their bounding boxes
visible image
7.5 Colliding with asteroids
For our purpose, the second method is good enough. It actually makes no difference to the game whether we crash into one asteroid, or into two of them simultaneously. The rocket will explode just the same. The only question for us is, did we intersect with any asteroid at all?
Thus, we shall use the second method. Since it returns an Actor , rather than a List , it is slightly simpler to work with. It will return an actor if we do have an intersection, or null if we currently do not intersect with any asteroid. We can check for a null return value to see whether we crashed into anything:
Actor a = getOneIntersectingObject(Asteroid. class ); if (a != null ) {
Exercise 7.30 Add a check for intersecting with an asteroid, similar to the one shown here, to your own checkCollision method.
Exercise 7.31 Add code to the body of the if statement that adds an explosion to the world at the current position of the rocket, and removes the rocket from the world. (To do this, you need to use the getWorld() method to access its methods for adding and removing objects from the world.)
For the last exercise above, we can use our own getX() and getY() methods to retrieve our current position. We can use this as the coordinates for placing the explosion.
An attempt at solving this might look like this:
World world = getWorld(); world.removeObject( this ); // remove rocket from world world.addObject( new Explosion(), getX(), getY());
This code looks reasonable at first glance, but will not work.
Exercise 7.32 Try out the code as shown above. Does it compile? Does it run? At what point does something go wrong, and what is the error message?
The reason this does not work is that we are calling the getX() and getY() methods after removing the rocket from the world. When an actor is removed from the world, it does not have any coordinates anymore—it has coordinates only while being in the world. Thus, the getX() and getY() method calls fail in this example.
This can easily be fixed by switching the last two lines of code: Insert the explosion first, and then remove the rocket from the world.
| Chapter 7 ■ Collision detection: Asteroids
Exercise 7.33 This is a very advanced exercise, and you may want to skip it initially, and come back to it later.
The explosion used here is a fairly simple looking explosion. It is good enough for the moment, but if you want to create really good looking games, it can be improved. A more sophisticated way to show explosions is introduced in a Greenfoot tutorial video, available on the Greenfoot web site:
http://www.greenfoot.org/doc/videos.html Create a similar explosion for your rocket.