You are here: JGT Tutorial > 8. In the line of fire

In the line of fire

We now write the code to allow the enemies to fire at us. We'll write a short class for these bullets, make the enemies fire, change the main loop, and add collision detection.

Let's first write a new class for the enemy bullets. We can inherit from the normal bullets and just change the color and direction. We'll also slow them down and add a check for the bottom of the screen.

/** The bullets that the enemies fire */
public class EnemyBullet extends Bullet {

   protected int _height;
   
   public EnemyBullet(VisibleObject shooter, HasSize world) {
      super(shooter, new MoveStraight(0,4));
      y=shooter.y+shooter.height;
      _color = Color.RED;
      _height = world.getHeight();
   }

   public void move() {
      super.move();
      if (y>_height) _alive=false;
   }
}
Notice that we added a new constructor for the normal bullet, so that we don't have to create two MoveControls. Here's how we changed the constructor code.
/** The bullets that the spaceship fires **/
public class Bullet extends VisibleObject {
    (...)
    
    public Bullet(VisibleObject shooter) {
       this(shooter, new MoveStraight(0,-8));
    }
    
    protected Bullet(VisibleObject shooter, MoveControl move) {
       (...)
       _move = move;
    }
}
As you can see we created the new two-argument constructor to allow the caller to prevent us from creating the MoveStraight. All the code that was in the old constructor is now in the new two-argument constructor.

The next step is to allow the enemies to fire. We use a FireControl as we did for the spaceship, but this time we use the FireRandom class. It fires at random, with the probability we give in the constructor. I used a fairly high number so we don't have to wait to see that the enemies fire. Use a smaller number to make the enemies shoot less often. The new fire() method creates an EnemyBullet and adds it to the game.

public class Enemy extends VisibleObject {

    (...)
    protected MyGame _world;
    protected FireControl _fire;

    public Enemy2(MyGame world, int x0, int y0) throws Exception {
        (...)
        _world = world;
        _fire = new FireRandom(0.02);
    }
    
    public void move() {
        // change x and y
        _move.move(this);
        if (_fire.shouldFire()) fire();
    }
    
    public void fire() {
       _world._enemyBullets.add(new EnemyBullet(this,_world));   
    }
    
    (...)
}
To fire, we add the EnemyBullet to the _enemyBullets list in the main class. For this to work we must of course add this list and make sure we draw and move all these new bullets. So we change the code of MyGame as follows.
public class MyGame7 extends BaseApp {

   Collection _enemyBullets;
   
   (...)
   
   /** main game loop **/
   public void run() {
      (...)
      
      _enemyBullets = new ArrayList<VisibleObject>();
      
      while (_gameRunning) {
         
         // move both kinds of bullets
         for (Iterator<VisibleObject> it = new UnionIterator<VisibleObject>(_bullets,_enemyBullets);it.hasNext();) {
            VisibleObject b = it.next();
            b.move();
            if (!(b._alive)) it.remove();
         }
         (...)
      }
   }
   
   /** draw what the player sees **/
   public void drawWorld() {
      (...)
      
         for (VisibleObject b : _enemyBullets) {
            b.draw(_dbg);
         }
         
       (...)
   }
}
The code above creates the new list and goes through it as expected. The interesting part is that we use the JGT's UnionIterator to go through both lists at once. We could also go through them separately if we prefered.

Now we must detect when the ship is hit. We use CollisionDetection. again for that purpose. The only catch is that it requires a list, so we create _shipList, a list that only contains our ship. Now we can use it to find collisions.

public class MyGame extends BaseApp {

   Collection _shipList;
   (...)
   
   
   /** main game loop **/
   public void run() {
      try {
      _ship = new Ship(this,getKeyboardStatus());
      _shipList = new ArrayList();
      _shipList.add(_ship);
      (...)
      
         
         collision = com.jpemartin.jgt.CollisionDetection.findCollisionListSlow(_enemyBullets,_shipList);
         if (null!=collision && !collision._a.isEmpty()) {
            System.out.println("Boom! You died");
         }
      (...)
   }
}
The code above does nothing much when we're hit. Instead, we want it to start the game over. To do this we split the main loop into its own method and call it from within run(). We add a loop, so now if you're hit all we have to do is call return for the game to start over.
   /** game entry point **/
   public void run() {
      try {
         while (_gameRunning) {
           (...)
           playLevel();
         }
      (...)
   }

   /** main game loop **/
   public void playLevel() {
      while (_gameRunning) {
         (...)
         collision = com.jpemartin.jgt.CollisionDetection.findCollisionListSlow(_enemyBullets,_shipList);
         if (null!=collision && !collision._a.isEmpty()) {
            System.out.println("Boom! You died");
            // done with the level
            return;
         }
      }
   }
Very little of what we did is visual, so all that the screenshot has to show is the new red shots from the enemies. But every other lesson has a screenshot at the end, so let's follow tradition.

Of course just starting over when you die is a little rough, we'll have to add more to this later. For now though, we move on to the next lesson: sounds and explosions.

Next: 9 - Sounds and explosions