Chapter 11. Galactic War: From Vectors to Bitmaps

The Galactic War project will demonstrate just one type of game that can be created in Java. This game is complex, but that complexity is hidden inside a game engine that, once written, does not need to be opened again. You will write an applet that will inherit from the game engine, and then the vast majority of the core code for the game will be handled behind the scenes. We’ll build the game step by step, beginning with the simplistic Asteroids-style game from Chapter 3, gradually improving the game until it is finished and ready to be put up on your website. The first step to building Galactic War is to begin converting the original project from an entirely vector-based game into a bitmap-based game. We’ll start with a partial conversion in this chapter, retaining some of the vector shapes but replacing the player’s ship with a bitmap.

Here are the key topics:

  • Improving the game

  • Generalizing the vector classes

Improving the Game

Chapter 3 gave you an example of a semi-complete Asteroids-style game to show you what would be covered in the upcoming chapters. You have now learned enough about game programming in Java to greatly enhance the game. That original game featured a class called BaseVectorShape, which contained all of the properties needed to manipulate game objects on the screen (the asteroids, bullets, and ship). In this chapter, we’ll make a few changes to add sprite support, and in the next chapter we’ll move the game entirely over to bitmaps.

By upgrading the spaceship to an image and doing away with the vector ship (which was little more than a filled triangle), the game is really starting to look more playable. There is no substitute for bitmapped graphics. But one of the goals I set out to achieve for this game is adding the ability to use an image instead of a shape, while still retaining the existing transformation features (most importantly, real-time rotation).

You can open up the project from Chapter 3 and continue using it or you can just copy the .java files from the old project to the new project. Here are the source code files you will want to bring over to the new game from previous chapters:

  • BaseVectorShape.java

  • Asteroid.java

  • Bullet.java

  • Ship.java

  • SoundClip.java

  • MidiSequence.java

Note that I did not include the main source code file, Asteroids.java. There are a few changes needed to add image support to the game, so I will just give you the complete source code listing for the main code file, which is now called GalacticWar.java.

Generalizing the Vector Classes

In the Asteroids project in Chapter 3, there were several classes to handle the ship, asteroids, and bullets in the game. Now we’re going to generalize these three classes and make them more general purpose, since a lot of code is shared among these classes. The Ship class will be replaced entirely with an ImageEntity (covered back in Chapter 6). Let me show you what we’re going to do with the Asteroid and Bullet classes.

Create a new source code file called VectorEntity.java. This class is very simple, as the following code suggests. Note that this class inherits from BaseGameEntity!

Note

The BaseGameEntity and ImageEntity classes were created back in Chapter 6. Despite our having already created an awesome animation class in Chapter 7 called AnimatedSprite, we learned that this class does not support transformations. Since we desperately need transformations for Galactic War, we will have to rely primarily on the original Sprite class and a less capable AnimatedSprite class that uses a scratch pad image to support both transforms and animation. This is the more advanced class that was hinted about in Chapter 7! Despite sharing the name, this is not the same class anymore.

// Vector class for handling game entities
import java.awt.*;

public class VectorEntity extends BaseGameEntity {
    //variables
    private Shape shape;

    //accessor methods
    public Shape getShape() { return shape; }

    //mutator methods
    public void setShape(Shape shape) { this.shape = shape; }

    //default constructor
    VectorEntity() {
        setShape(null);
    }
}

The New Asteroid Class

The Asteroid class will be modified now to use VectorEntity as a base class. This frees up a lot of code that was previously duplicated in Asteroid and the other classes. The Asteroid class inherits from VectorEntity, which in turn inherits from BaseGameEntity. You can open up the Asteroid.java file that you copied over from the project in Chapter 3, or you can just add this as a new class to the Galactic War project.

// Asteroid class derives from BaseVectorShape
import java.awt.*;

public class Asteroid extends VectorEntity {
    //define the asteroid polygon shape
    private int[] astx = {-20,-13, 0,20,22, 20, 12, 2,-10,-22,-16};
    private int[] asty = { 20, 23,17,20,16,-20,-22,-14,-17,-20, -5};

    //rotation speed
    protected double rotVel;
    public double getRotationVelocity() { return rotVel; }
    public void setRotationVelocity(double v) { rotVel = v; }

    //bounding rectangle
    public Rectangle getBounds() {
        Rectangle r;
        r = new Rectangle((int)getX() - 20, (int) getY() - 20, 40, 40);
        return r;
    }

    //default constructor
    Asteroid() {
        setShape(new Polygon(astx, asty, astx.length));
        setAlive(true);
        setRotationVelocity(0.0);
    }
}

The New Bullet Class

Much of the code in the previous Bullet class has now been moved to VectorEntity as well, so we can just rewrite this class and give it the specific information relevant to a bullet object (most notably, that the getBounds() method returns a Rectangle that is one pixel wide and one pixel high).

// Bullet class derives from BaseVectorShape
import java.awt.*;

public class Bullet extends VectorEntity {

    //bounding rectangle
    public Rectangle getBounds() {
        Rectangle r;
        r = new Rectangle((int)getX(), (int) getY(), 1, 1);
        return r;
    }

    Bullet() {
        //create the bullet shape
        setShape(new Rectangle(0, 0, 1, 1));
        setAlive(false);
    }
}

The Main Source Code File: GalacticWar.java

The actual gameplay hasn’t changed much in this new revision. Figure 11.1 shows the game with a new bitmap image being used for the player’s spaceship. However, we have upgraded the core classes significantly in this update, which will be very useful in the next chapter, where ImageEntity will see a lot more use.

The player’s spaceship is now a bitmap image rather than a polygon.

Figure 11.1. The player’s spaceship is now a bitmap image rather than a polygon.

The ImageEntity, which you learned about in Chapter 6, provides a getBounds() method to perform collision testing. You can still toggle the bounding rectangles on and off by pressing the B key. Figure 11.2 shows the rectangle around the player’s ship. The collision code is a little too strict for a truly enjoyable game because the bounding rectangles are slightly too big. We’ll correct this by finetuning the collision code in the next two chapters.

The bounding rectangles are used for collision testing.

Figure 11.2. The bounding rectangles are used for collision testing.

The majority of the code remains unchanged from the Asteroids.java file back in Chapter 3, so you can just open that file and modify it as indicated. But I’m going to list the entire program again because it’s been a long time since we went over that code. I have highlighted in bold all of the lines of code that have changed. If the source code for a particular method has not changed at all, I simply commented out the code and inserted the statement //no changes needed, so keep an eye out for this comment and then reuse that code from the Chapter 3 project. It is a beautiful testament to object-oriented programming that so few changes are needed to this source code file!

// GALACTIC WAR, Chapter 11
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;

// Primary class for the game
public class GalacticWar extends Applet implements Runnable, KeyListener {
    //the main thread becomes the game loop
    Thread gameloop;
    //use this as a double buffer
    BufferedImage backbuffer;
    //the main drawing object for the back buffer
    Graphics2D g2d;
    //toggle for drawing bounding boxes
    boolean showBounds = false;

    //create the asteroid array
    int ASTEROIDS = 20;
    Asteroid[] ast = new Asteroid[ASTEROIDS];

    //create the bullet array
    int BULLETS = 10;
    Bullet[] bullet = new Bullet[BULLETS];
    int currentBullet = 0;

 //the player's ship
        ImageEntity ship = new ImageEntity(this);

    //create the identity transform
    AffineTransform identity = new AffineTransform();

    //create a random number generator
    Random rand = new Random();

    //load sound effects
    SoundClip shoot;
    SoundClip explode;
  // applet init event
 public void init() {
     //create the back buffer for smooth graphics
     backbuffer = new BufferedImage(640, 480, BufferedImage.TYPE_INT_RGB);
     g2d = backbuffer.createGraphics();

     //set up the ship
     ship.setX(320);
     ship.setY(240);
     ship.load("spaceship1.png");
     ship.setGraphics(g2d);

    //set up the bullets
    for (int n = 0; n<BULLETS; n++) {
        bullet[n] = new Bullet();
    }

    //set up the asteroids
    for (int n = 0; n<ASTEROIDS; n++) {
        ast[n] = new Asteroid();
        ast[n].setRotationVelocity(rand.nextInt(3)+1);
        ast[n].setX((double)rand.nextInt(600)+20);
        ast[n].setY((double)rand.nextInt(440)+20);
        ast[n].setMoveAngle(rand.nextInt(360));
        double ang = ast[n].getMoveAngle() - 90;
        ast[n].setVelX(calcAngleMoveX(ang));
        ast[n].setVelY(calcAngleMoveY(ang));
    }

    //load sound files
    shoot = new SoundClip("shoot.wav");
    explode = new SoundClip("explode.wav");

    //start the user input listener
    addKeyListener(this);
}

  // applet update event to redraw the screen 
 public void update(Graphics g) {
    //NO CHANGES HERE
}
// drawShip called by applet update event
public void drawShip() {
     //transform and draw the ship
     ship.transform();
     ship.draw();

    //draw bounding rectangle around ship
    if (showBounds) {
        g2d.setTransform(identity);
        g2d.setColor(Color.BLUE);
        g2d.draw(ship.getBounds());
    }
}

There are no changes beyond this point. Please double-check the source code listing in your new Galactic War project to ensure that all of the methods following this point are included (from the project in Chapter 3). If you prefer, you may open the completed project in the resource folder sourceschapter11 GalacticWar (found at www.courseptr.com/downloads).

   // drawBullets called by applet update event
   public void drawBullets() {
       //NO CHANGES HERE
   }
   // drawAsteroids called by applet update event
   public void drawAsteroids() {
       //NO CHANGES HERE
   }
   // applet window repaint event--draw the back buffer
   public void paint(Graphics g) {
       //NO CHANGES HERE
   }
   // thread start event - start the game loop running
   public void start() {
       //NO CHANGES HERE
   }
   // thread run event (game loop)
   public void run() {
       //NO CHANGES HERE
   }
       // thread stop event
       public void stop() {
           //NO CHANGES HERE
       }
       // move and animate the objects in the game
       private void gameUpdate() {
           //NO CHANGES HERE
       }
       // Update the ship position based on velocity
       public void updateShip() {
           //NO CHANGES HERE
       }
       // Update the bullets based on velocity
       public void updateBullets() {
           //NO CHANGES HERE
       }
       // Update the asteroids based on velocity
       public void updateAsteroids() {
           //NO CHANGES HERE
       }
       // Test asteroids for collisions with ship or bullets
       public void checkCollisions() {
           //NO CHANGES HERE
       }
   
       // key listener events
       public void keyReleased(KeyEvent k) { }
       public void keyTyped(KeyEvent k) { }
       public void keyPressed(KeyEvent k) {
           //NO CHANGES HERE
       }
   
       // calculate X movement value based on direction angle
       public double calcAngleMoveX(double angle) {
           //NO CHANGES HERE
       }
   
       // calculate Y movement value based on direction angle
       public double calcAngleMoveY(double angle) {
           //NO CHANGES HERE
       }
   }

What You Have Learned

You have now learned the most difficult and challenging aspects of writing a Java game at this point, and you are ready to start heading down the hill at a more leisurely pace in the upcoming chapters. This chapter explained:

  • How to add bitmaps to Galactic War

  • How to support multiple key presses

Review Questions

The following questions will help you to determine how well you have learned the subjects discussed in this chapter. The answers are provided in Appendix A, “Chapter Quiz Answers.”

1.

What is the name of the class that handles bitmaps?

2.

Which class in Galactic War detects when bullets hit the asteroids?

3.

What is the maximum number of sprites that can be supported by the game?

4.

Which method in the Graphics2D class actually draws the image of a sprite?

5.

What is the name of the Applet method that redraws the window?

6.

How many key presses can the game detect at a single time?

7.

What method do you use to track the mouse’s movement?

8.

What type of graphics entity does the game use for the asteroids?

9.

Regarding ship rotation, by how many angles can the ship be rotated?

10.

What method provides the game with support for collision detection?

On Your Own

The following exercise will test your comprehension of the topics covered in this chapter by making some important changes to the projects.

The Galactic War game is much more playable now than it was back in Chapter 3, thanks in part to the new keyboard handler. But we are completely ignoring a perfectly valid alternative to the keyboard—your trusty mouse. Devise a way to add mouse support to the game. You could rotate the ship when the mouse is moved left or right, and apply thrust when the mouse wheel is used, and fire when the button is pressed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.5.166