You are here: JGT Tutorial > 5. Enemies

Lesson 2: Enemies and bullets

Now that we have a spaceship that can move about, let's give it things to fight! We'll start by adding an enemy spaceship, and then we'll get a gun.

Enemies

In the same way that our own spaceship was its own class, we're going to create a new class for the enemy spaceship. It is very similar to our own, except that of course we're drawing a different-looking ship. Also, the movement of this ship is not controlled by the keyboard, but instead we're using a left-to-right movement pattern that comes with the Java Game Toolkit, the MoveLeftRight class. The code below shows the relevant portions of the enemy spaceship. Note the similarity with our own, but also how we use the MoveControl and the Drawable differently. Another small difference is that the constructor now takes the initial position as argument.
public class Enemy extends com.jpemartin.jtb.VisibleObject {

    (...)
    
    protected static final int _speed = 2;

    public Enemy(HasSize world, int x0, int y0) throws Exception {
        init(world,x0,y0);
    }    
    
    /** all one-time initialization, called by the ctor. **/
    protected void init(HasSize world, int x0, int y0) throws Exception {
        _alive=true;
        // get enemy to initial state
        x = x0;
        y = y0;
        _move = new MoveLeftRight(world.getWidth(),_speed);
        _draw = new DrawAnim("gamedata/gpatrol_b32.png");
    }
    
    (...) // move and draw are the same as Ship's
    
}
We also modify the main loop to create the Enemy object and call its move and draw method for every frame. Here is a screenshot of the resulting game.

To make the game challenging we don't want just one enemy, rather many of them! This is easy to program because we have a separate class for the enemy. What we have to do is just create several instances of Enemy, and they will all behave independently. We need these enemies to be at different positions on the screen so we can see them, so we use the fact that the constructor of Enemy allows one to set the initial position.

The code below shows the relevant parts of the main program, and a screenshot. Note that we have a list of Enemy objects (in _enemies). We create 5 enemy ships at the beginning of the run() method. Both run() and drawWorld() use a loop to go through all the enemies.

public class MyGame extends BaseApp {

   Collection<Enemy> _enemies;

   (...)   
   
   /** main game loop **/
   public void run() {
      try {
      _ship = new Ship(this);
      _enemies = new ArrayList<Enemy>();
      for (int i=0; i<5; i++) {
         _enemies.add( new Enemy(this, 10+i*20, 20+40*i));
      }
      
      createImageAndGraphics();
      while (_gameRunning) {
         for (Enemy e : _enemies) {
            e.move(this);
         }
         _ship.move(this);
         drawWorld();
         showAndWait();
      }
      } catch (Exception x) {
         x.printStackTrace();
         throw new RuntimeException("Sorry, there's some sort of bug",x);
      }
   }
   
   /** draw what the player sees **/
   public void drawWorld() {
      if (null==_dbImage) return;
      synchronized(_dbImage) {
         _dbg.setColor(Color.BLACK);
         _dbg.fillRect(0, 0, getWidth(), getHeight());
         for (Enemy e : _enemies) {
            e.draw(_dbg);
         }
         _ship.draw(_dbg);
      }
   }
   
   (...)
   
}

Now we have a nice set of enemies! Our code is a little inefficient because it loads the graphics for the enemy several times (it loads it every time the constructor is called). We can make the code more efficient by making the _draw variable static, and initializing it only once. The code below shows the relevant changes to Enemy.

public class Enemy extends com.jpemartin.jtb.VisibleObject {

    protected static Drawable _draw;
    
    (...)
    
    /** all one-time initialization, called by the ctor. **/
    protected void init(HasSize world, int x0, int y0) throws Exception {
        (...)
        if (null==_draw) {
           _draw = new DrawAnim("gamedata/gpatrol_b32.png");
        }
    }
    
    (...)
}

Next: 6