Sunday, August 15, 2010

Simple Collision Detection in Java

Collision detection is a basic element of a video game. It is one of the primary means of determining when objects in a game should interact. In Asteroids, there are collisions between the player's shot and the asteroids themselves. There are collisions between the player's shot and the alien spacecraft (hopefully.) There are collisions between the asteroids and the player's ship (argh!) Each of these is critical to the game.

Screen shot of collision detection in the video game kernel, showing color change of ball when intersecting the brick.

Since we don't have actual physics to determine when there's been a collision, as we have in real life, we need to create a set of rules for when a collision has occured then implement those rules in our code. One way to handle collision detection is to see if two potentially colliding objects intersect at all on the playfield. That is, whether one of them overlays the other.

Previously, we have worked with a simple video game kernel, then updated it to make the addition of game logic simpler. We had also created a Brick class that we can use to interact with the ball in our basic video game kernel.

Here is a slightly changed version of the simple video game kernel. It uses the Brick class, so Brick.class should be in the same directory as your VGBKernel.java and VGBKernel.class (it wouldn't be a bad idea to have Brick.java there, too, in case you want to play around with it at all to get different effects with this program through changes of your own.)
/* A simple video game style kernel, revision 3.
by Mark Graybill, August 2010
Uses an inner class to contain game logic,
with another inner class as a game timer.
*/

// Import Timer and other useful stuff:
import java.util.*;
// Import the basic graphics classes.
import java.awt.*;
import javax.swing.*;
import java.lang.Math;

public class VGBKernel extends JPanel{

public Rectangle screen, bounds; // The screen area and boundary.
public JFrame frame; // A JFrame to put the graphics into.
public VGTimerTask vgTask; // The TimerTask that runs the game.
public VGBall ball; // The game ball, a subclass of Rectangle.
private Brick brick; // A brick for the ball to interact with.

// Create a constructor method:
public VGBKernel(){
super();
screen = new Rectangle(0, 0, 600, 400);
bounds = new Rectangle(0, 0, 600, 400); // Give some temporary values.
ball = new VGBall();
frame = new JFrame("VGBKernel");
vgTask = new VGTimerTask();
brick = new Brick();
}

// Create an inner TimerTask class that has access to the
// members of the VGBKernel.
class VGTimerTask extends TimerTask{
public void run(){
ball.move();
frame.repaint();
}
}

// Create an inner VGBall class that has our game logic in it.
class VGBall extends Rectangle{
int xVel, yVel; // The ball's velocity.
Color ballColor; // The color of the ball.

public VGBall(){
super(0, 0, 20, 20);
xVel = width/4;
yVel = height/4;
ballColor=new Color(0, 0, 128);
}

// Instance methods for VGBall
public void move(){
// Move the ball according to the game rules.
x+=xVel; // Move horizontally.
y+=yVel; // Move vertically.
// Detect edges and bounce if necessary.
if (x > (bounds.width - width)){
xVel = -xVel; // reverse movement.
x = bounds.width - width; // Set location to screen edge.
}
if (y > (bounds.height - height)){
yVel = -yVel; // reverse movement.
y = bounds.height - height;
}
if (x <= 0) { xVel = -xVel; x = 0; }
if (y <= 0) { yVel = -yVel; y = 0; }

// Check for intersection with Brick,
// change color when touching.
if (intersects(brick)) { ballColor=Color.GREEN; }
else { ballColor=Color.BLUE; }

}

public void draw(Graphics g){
// the ball draws itself in the graphics context given.
Color gcColor = g.getColor(); // Preserve the present color.
g.setColor(ballColor); // Use the ball's color for the ball.
g.fillRect(x, y, width, height); // Draw the ball.
g.setColor(gcColor); // Restore prior color.
} // end draw()

} // end of class VGBall

// Now the instance methods:
public void paintComponent(Graphics g){
// Get the drawing area bounds for game logic.
bounds = g.getClipBounds();
// Clear the drawing area.
g.clearRect(screen.x, screen.y, screen.width, screen.height);
// Draw the brick.
g.setColor(brick.getColor());
g.fillRect(brick.x, brick.y, brick.width, brick.height);
// Draw the ball.
ball.draw(g);
}


public static void main(String arg[]){

java.util.Timer vgTimer = new java.util.Timer(); // Create a Timer object
VGBKernel panel = new VGBKernel();

panel.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.frame.setSize(panel.screen.width, panel.screen.height);

panel.frame.setContentPane(panel);
panel.frame.setVisible(true);

// Set up the brick.
panel.brick.x = panel.screen.width/4;
panel.brick.y = panel.screen.height/4;
panel.brick.width = panel.screen.width/2;
panel.brick.height = panel.screen.height/2;

// Set up a timer to do the vgTask regularly.
vgTimer.schedule(panel.vgTask, 0, 33);
}
}

The main changes here are the addition of the brick to the VGBKernel (that's why I renamed VGKernel to VGBKernel, by the way--it's now a Video Game Brick Kernel), and I've changed ball into an extension of Rectangle to make collision detection a bit easier.

To do the actual collision detection, I'm using the method intersects() from the Rectangle class. It tests whether one Rectangle overlaps another in the coordinate space.

In this case, if the ball is on top of the brick (a rather large brick), then the ball turns green. I could have had any of a number of other effects. For example, when I was first testing this routine I didn't change the color of the ball, instead I had the ball send a message to the Java console that read "Ow!". So whenever the ball passed over the brick I'd get a long string of messages in the console like this:

Ow!
Ow!
Ow!
Ow!
Ow!
Ow!
Ow!
Ow!
Ow!

I could just as well have the program play a sound, draw an image, update a score, or any of a number of different things when a collision occurs. Exactly what should happen is determined by the rules of the video game itself.

Going Further

Try changing the effect when the ball strikes the brick. The easiest place to start would be making the ball a different color than green. To get more sophisticated, try things like adding a sound, or counting the number of moves that the ball is in contact with the brick. Change the size and location of the brick. Perhaps try adding additional bricks, and have each create a different effect. Try changing the color of the brick when the ball strikes it.

One thing about the collision detection we are doing here. It only detects when we have actually started to overlap with the brick. In many cases, it's desirable to predict when a collision will occur as a result of a move before that move occurs. Try to develop a solution to this yourself. I'll be addressing this in a future article, so you can compare what you came up with against my method. Remember that in programming there's almost never just one way to accomplish something. The same effect can usually be produced any of a number of different ways effectively enough to use in a finished program.
StumbleUpon