Showing posts with label inner class. Show all posts
Showing posts with label inner class. Show all posts

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

Wednesday, August 11, 2010

Java's Inner Classes: The Keys to the Kingdom

In my two most recent code examples I've used inner classes in the program.

Java Video Game Programming: Game Logic

A Simple Java Video Game Kernel

An inner class is a Java class that's defined inside another class. In both the examples above, I have a class called VGKernel, which is the class that implements the video game kernel. Inside that class, I define other classes. This has a special effect on the relationship between those classes and VGKernel.

Normally, our objects are encapsulated, that is, their members (variables and methods) are inaccessible to other classes unless they've been marked as public, and in those cases we take care to make sure that the object can't be messed up using those public members.

Some things in Java don't work very well with the encapsulation. Graphics and Threads are two of them. Fortunately, there's a way around the encapsulation. It lets one object access all the members of another object as if it owned them.

The downside is that this can be a dangerous coding practice, like global variables (check out the sort of problems  JavaScript's had with security, if you want to know why global variables are a problem.) Your inner class has complete control over the outer class. Root access, keys to the kingdom. It could wear the outer class like the alien guy wore the redneck's skin in Men in Black.

If used well inner classes solve a multitude of problems. In the case of the two versions of VGKernel, it solves the problem of the ball being able to access the graphical context (at present, the Ball class doesn't really need to, I'll admit, but we'll see more of why it does this as the code develops in future articles), and it allows VGTimerTask to access the methods of VGKernel.

The Java Tutorial has a pretty good section on inner classes, as one type of nested class.
StumbleUpon

Monday, August 9, 2010

Java Video Game Programming: Game Logic

The simple video game kernel I presented earlier is a good start, but some compromises were made to make it as simple as possible. Now we're going to start adding back some of the complexity to give us more control over what we can do with it.

The first step is to make the game's ball a real object, rather than some variables within our JFrame.

In simple video games, there is usually some object in the game that does most of the "thinking" with respect to the game's rules. In a game like Pong or Breakout, the ball does most of the "thinking". The paddles move according to user input, but they don't really need to know if they've collided with anything in the game (their own logic keeps them from moving off the playfield.) The bricks don't need to know anything about what's going on, they just need to hang around, then disappear when something tells them to, and perhaps return some information about what score they're worth, if there are different scores for hitting different bricks.

The ball, however, is a more active participant in the game. It needs to know about collisions, respond to the other objects in the game, and generally know what's going on. In other words, it is the object that keeps track of most of the game rules. In a video game, this is referred to as the game logic.


A bouncing ball in the simple Java video game kernel program.

This simple kernel improves on the original. Turn it into your own Pong, Breakout, or Tank clone.

In the example below I've made a Ball class. This is where most of the game's logic will reside. Here it is in the ball's move() method. Since the ball will need access to inside information about the playfield and the objects on it, I've made it an inner class of the playfield's class (VGKernel.) As an inner class, it has access to all of VGKernel's members and methods as if it owned them itself. This simplifies the program a lot.

Here is the modified version of the video game kernel. Changes from the original have been highlighted:
/* A simple video game style kernel, revision 2.
   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 VGKernel extends JPanel{

// This is not a recommended coding practice, just a shortcut.
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.
// Create a constructor method:
  public VGKernel(){
    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("VGKernel");
    vgTask = new VGTimerTask();
}

  // Create an inner TimerTask class that has access to the
  // members of the VGKernel.
  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{
    // Accessor methods would be more proper,
    // but for now I just make the needed variables public.
    public int x, y, width, height; // Ball's location and size.
    int xVel, yVel; // The ball's velocity.

    public VGBall(){
      x = 0;
      y = 0;
      width = 20;
      height = 20;
      xVel = width/4;
      yVel = height/4;
    }
    // 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; }
    }
  } // 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, then draw the ball.
    g.clearRect(screen.x, screen.y, screen.width, screen.height);
    g.fillRect(ball.x, ball.y, ball.width, ball.height);
  }

  public static void main(String arg[]){

    java.util.Timer vgTimer = new java.util.Timer();  // Create a Timer object
    VGKernel panel = new VGKernel(); 
    
    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 a timer to do the vgTask regularly.
    vgTimer.schedule(panel.vgTask, 0, 33);
  }
}
Now the ball not only is its own class, it has a more sophisticated means of keeping track of its direction of movement (xVel and yVel, tracking velocity as a positive or negative value, as opposed to the simple right and down booleans in the original.) This means that logic in the game could vary the velocity of the ball easily, as well as change its direction of movement.

Other objects on the playfield should be defined in their own classes, outside VGKernel. This will keep VGKernel from becoming too long and from making the program too monolithic and difficult to maintain.

Simple exercises:
Since the ball has access to VGKernel's members, including the drawing screen, the ball can be made to draw itself. By giving the ball its own draw() method, we can make it easier to change the appearance of the ball at will. Give it a try.
StumbleUpon

Wednesday, August 4, 2010

A Simple Java Video Game Kernel

Most video games run continuously, rather than waiting for user input before they do something. The heart of a video game of this sort is called the kernel. The kernel is running in the background, collecting user input and updating the display. It uses logic to determine if the game has been won or lost or otherwise control the state of the game.


A bouncing ball in the simple Java video game kernel program.
This simple kernel sends a ball bouncing around on the screen. Turn it into your own Pong, Breakout, or Tank clone.

To run this way, Threads are usually used to allow more than one thing to be going on at a time in a Java program. We've looked at a simple way of using threads before, the Timer class.

Here's a really, really simple video game kernel. It has all the basic elements of a video game.
/* A simple video game style kernel
   by Mark Graybill, August 2010
   Uses the Timer Class to move a ball on a playfield.
*/

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

public class VGKernel extends JPanel{

// Set up the objects and variables we'll want.
public Rectangle screen, ball; // The screen area and ball location/size.
public Rectangle bounds;  // The boundaries of the drawing area.
public JFrame frame; // A JFrame to put the graphics into.
public VGTimerTask vgTask; // The TimerTask that runs the game.
public boolean down, right; // Direction of ball's travel.

// Create a constructor method that initializes things:
  public VGKernel(){
    super();
    screen = new Rectangle(0, 0, 600, 400);
    ball   = new Rectangle(0, 0, 20, 20);
    bounds = new Rectangle(0, 0, 600, 400); // Give some starter values.
    frame = new JFrame("VGKernel");
    vgTask = new VGTimerTask();
}
  // Create an inner TimerTask class that has access to the
  // members of the VGKernel.
  class VGTimerTask extends TimerTask{
    public void run(){
      moveBall();
      frame.repaint();
    }
  }

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

  public void moveBall(){
  // Ball should really be its own class with this as a method.
    if (right) ball.x+=ball.width; // If right is true, move ball right,
    else ball.x-=ball.width;       // otherwise move left.
    if (down)  ball.y+=ball.height; // Same for up/down.
    else ball.y-=ball.width;
    if (ball.x > (bounds.width - ball.width)) // Detect edges and bounce.
      { right = false; ball.x = bounds.width -  ball.width; }
    if (ball.y > (bounds.height - ball.height))
      { down  = false; ball.y = bounds.height - ball.height;}
    if (ball.x <= 0) { right = true; ball.x = 0; }
    if (ball.y <= 0) { down  = true; ball.y = 0; }
  }

  public static void main(String arg[]){
    java.util.Timer vgTimer = new java.util.Timer();  // Create a Timer.
    VGKernel panel = new VGKernel(); // Create and instance of our kernel.
    
    // Set the intial ball movement direction.
    panel.down = true;
    panel.right = true;

    // Set up our JFRame
    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 a timer to do the vgTask regularly.
    vgTimer.schedule(panel.vgTask, 0, 100);
  }
}

This example can be expanded with methods to get control inputs, additional players on the playfield (like paddles), and logic to determine when someone scores.

The code here is far from perfect, but I've made some compromises to make things as simple as I could while still showing a full working example. Not that any code that runs and does what is supposed to is really bad, but there are other, better ways of doing this. But this works and is fairly easy to understand.

What the program does is create a JPanel that has an inner class (a class defined within itself) of VGTimerTask. The VGTimerTask is a kind of TimerTask, which can be scheduled to occur on a regular basis by a Timer. Since VGTimerTask is an inner class of VGPanel, it has access to all the members of VGPanel. This is critical. Without that, it wouldn't be able to access the ball and redraw the screen easily (it can still be done, but in a more complex way.)

Timer is a decent way of running a simple game, but more complex games should use some other timing mechanism. java.util.Timer is affected by a number of outside events, so to get smoother, more reliable timing you a timer like the one in the Java3D package would work better.

A Simple Improvement

There are many ways of improving on this basic example. One way that is very simple is to smooth the animation. The movement of the ball is pretty jerky. This is caused by both the distance that the ball moves each "turn", and by the time between screen updates. We can smooth out the animation by addressing both of these.

First, let's change moveBall() to shift the ball a smaller distance each time:
public void moveBall(){
  // Ball should really be its own class with this as a method.
    if (right) ball.x+=ball.width/4; // If right is true, move ball right,
    else ball.x-=ball.width/4;       // otherwise move left.
    if (down)  ball.y+=ball.height/4; // Same for up/down.
    else ball.y-=ball.width/4;
    if (ball.x > (bounds.width - ball.width)) // Detect edges and bounce.
      { right = false; ball.x = bounds.width -  ball.width; }
    if (ball.y > (bounds.height - ball.height))
      { down  = false; ball.y = bounds.height - ball.height;}
    if (ball.x <= 0) { right = true; ball.x = 0; }
    if (ball.y <= 0) { down  = true; ball.y = 0; }
  }
Now the ball is being moved only one quarter of its size each turn.

Next, change the Timer schedule to draw the screen every 20 milliseconds instead of every 100 milliseconds:
// Set up a timer to do the vgTask regularly.
    vgTimer.schedule(panel.vgTask, 0, 20);

Now you have a ball that moves a lot smoother.

I'll be expanding on this basic kernel and improving it in future articles, starting with Java Video game Programming: Game Logic
StumbleUpon

Sunday, July 20, 2008

Controlling Java's Graphics: paintComponent() and repaint()

Why do we put all our graphical drawing code into a paintComponent() method? It seems odd, since it would seem we should be able to simply stick some simple graphics commands into our main() method in a Java application and just get the drawing done. Where does paintComponent come from? If we never call it in our code, how does it get executed?

It seems like magic, doesn't it? You just sort of set it up and somehow it happens. Hopefully when and how we want it to. It seems like a vaguely disquieting form of magic, perhaps.

When Java does graphics, suddenly you're not in the strict control of the program that you are with command line applications. With graphics, your program is now interacting with the other parts of your computer's operating system. It's not just running in its own little corner of the machine. Now it's got a window out on that shared resource, the display, and the window has to interact properly with everything else that's on the display.

Think of it this way, if you are just driving around on private property all by yourself you don't have to worry about traffic. Off on your own stretch of road, with an unused parking lot or two to tool around in, you don't have to worry about sharing. You can cross the center line, pull on and off the road without signalling, you don't even need a license so long as you don't go on a public road.

But once you head for the public roads and freeway, you've got to share. The rules matter. It gets more complicated than buzzing around in a go-cart or whatever on private property. And things happen that you don't have any direct control over, like red lights and merging traffic.

When you move your program on to the display in Java, you don't have to do everything on your own as you would have to in a car. Java's graphics system takes care of a lot for you. Unfortunately this means your code starts looking like magic. Methods get called without you doing the calling. But it's OK. All you need to know is what to expect.

The paintComponent() method redraws your graphics area. In our examples so far this has been a JPanel. There are two ways paintComponent() can get called. One is automatic, the other is up to you.

The automatic way is what we've used so far. The computer's window manager automatically calls for your program to redraw its graphics whenever a window it owns appears to need redrawing. The window manager can decide this for any of a number of reasons. Such as when the window initially appears on screen. Or if the window has been covered up in part or in whole by another window on screen, and the covered part has just been uncovered. Maybe the window had been minimized, and has just been restored.

When these sort of things happen, the Java Runtime Environment calls the paint() method for your container. In our examples so far, our container has been a JFrame. It will be a "heavyweight" component (see the JFrames article for a brief discussion on this.) Once the container redraws itself, it'll pass the command down to its children, that is, the lightweight components that live inside it. A lightweight component from the Swing package uses the paintComponent() method to redraw itself. So that's what gets called. Shazam! It's like magic!

This means you want to set up paintComponent() to draw things the way you want them when it gets called. If you're doing drawings that don't change, like the examples we've done to date, then you don't need to look up any information. If you're doing anything animated or that otherwise changes over time, you need to have paintComponent() be able to look things up at the time of drawing to properly reflect conditions in the drawing.

Now, let's say you've changed something in your program and you want to redraw to show the change. You probably don't want to wait until the window manager thinks your window needs redrawing. If nobody is changing things on the screen otherwise, it may not redraw until way too late!

Does that mean you put a call to paintComponent() in your program? No! You would be taking a chance of breaking something if you did. You want to start from the top, like the Java Runtime Environment did. In your case, you want to call repaint(). This will take things from the top. It also handles some possibilities that jumping in and calling paintComponent() on your own will not.

Like, let's say both you and the window manager decide to redraw at the same time. If you bypass repaint() you'll be slowing down your program by making it repaint twice when it really only needs to redraw once. When you call repaint(), Java catches this for you and saves you some computer time by only redrawing once. It'll also catch redraw requests made while the redrawing is going on and deal with them appropriately. Your paintComponent() method will get called in its own turn when you use repaint().

So, if you have new information you want depicted on screen, use repaint().

Put all your drawing inside paintComponent() and let the system take care of knowing when to call it otherwise. Give paintComponent() the ability to get the information it needs to draw things the way you want them now. If you're getting ahead of what I've written here, and aren't sure how to get through the Object Oriented barriers, look up "inner classes" for a start. It gives you a way to get the information where you need it when you need it.
StumbleUpon