public class Bullet extends com.jpemartin.jgt.VisibleObject {
protected MoveControl _move;
protected Color _color;
public Bullet(VisibleObject shooter) {
_alive=true;
height=10;
width=4;
x = shooter.x+shooter.width/2-width/2;
y = shooter.y-height;
_color = Color.YELLOW;
_move = new MoveStraight(0,-8);
}
public void move() {
_move.move(this);
if (y+height<-10) _alive=false;
}
public void draw(Graphics g) {
g.setColor(_color);
g.fillRect(x,y, width, height);
}
}
The structure of the Bullet class should be familiar. The only novely here, other from the different way of drawing, is that we mark the bullet as "not alive" if it goes outside of the screen.
Before we write the code to make us shoot the bullets, we need to add a bit of code to the main loop that takes care of the bullet. The partial code below shows our changes: we add a collection called _bullets to hold the bullets.
We are treating the bullets separately because we'll want to detect when they collide something. We then change the main loop to move all bullets, and drawWorld() to draw all bullets.
public class MyGame extends BaseApp {
Ship _ship;
Collection _enemies;
Collection _bullets;
(...)
/** main game loop **/
public void run() {
(...)
_bullets = new ArrayList();
(...)
while (_gameRunning) {
for (Enemy e : _enemies) {
e.move();
}
for (Iterator<VisibleObject> it = _bullets.iterator();it.hasNext();) {
it.next().move();
if (!(it.next()._alive)) it.remove();
}
_ship.move();
drawWorld();
showAndWait();
}
(...)
}
/** draw what the player sees **/
public void drawWorld() {
(...)
for (VisibleObject b : _bullets) {
b.draw(_dbg);
}
(...)
}
}
The loop for moving bullets looks different from the other ones but it behaves very similarly. The only difference is that if a bullet is marked as "not alive" (_alive=false), it will be removed from the list. This makes sure that we don't keep track of bullets after they've gone beyond the top of the screen. We'll have to do the same thing for enemies later, when we want to be able to kill them.
Next we need a mechanism to shoot a bullet. Let's add a fire() method to our Ship class to do that:
public class Ship extends VisibleObject {
MyGame _main;
(...)
public Ship2(MyGame world, KeyboardStatus kbd) throws Exception {
_main = world;
(...)
}
public void fire() {
_main._bullets.add( new Bullet(this) );
}
}
As you can see, we have first modified Ship so it has direct access to the main game class, MyGame (in variable _main). The fire() method can then directly add a bullet to the list. Directly modifying structures from other classes is generally bad form, but in this case it allows us to keep the tutorial simple.
The final step is to call the fire() method when the player presses the space bar. Luckily there is already a mechanism in place in the JGT to handle this:
FireRepeat.
The FireRepeat class checks whether the user presses the space bar, and if so it will tell us repeatedly to fire. It does so by returning true when we query shouldFire().
So all we have to do is create this object and query it every turn. Doing this in the move() method is convenient since that method is already called every turn. The code below shows the changes.
public class Ship extends VisibleObject {
FireControl _fire;
(...)
public Ship2(MyGame5 world, KeyboardStatus kbd) throws Exception {
(...)
_fire = new FireRepeat(kbd);
}
public void move() {
// change x and y
_move.move(this);
if (_fire.shouldFire()) fire();
}
(...)
}
Our ship can now fire in addition to move! In the next lessons we'll make the bullets actually dangerous.