Planetalia - Java Training

Training and Consulting

Home

Tutorial - Writing a Space Invaders game in Java

  Previous Page - Controlling the player Current Page - 18 - Shots   Next Page - More shooting - Cluster bombs
  Return to the index of free Java tutorials  

Shots

(c) Alexander Hristov

Ok, it's finally time to add some violence to our - so far - pacific game. We will implement the ability of the player to shoot missiles. For now we will concentrate only on the missiles, their movement, how they appear and disappear and will leave their effects (killing monsters) for a later step.

Our missiles will follow these premises

  1. When fired, a missile appears just above the ship of the player
  2. All missiles move upwards, at constant speed
  3. There will be a limit in the number of missiles that can be on the screen

From the point of view of the game, shots are merely actors. The new thing about missiles is that they appear and disappear dynamically based on factors that are outside the control of the main game loop. For example, the main game loop does not control when the player chooses to fire a missile. However, when this happens, a new actor must be created and added to the global list of actors. The Player class gets the keyboard event, but the global actor list is responsability of the Invaders class.

A possible solution is giving the stage ability to add actors on the fly:


1     /**
2      * Curso B?sico de desarrollo de Juegos en Java - Invaders
3      * 
4      * (c) 2004 Planetalia S.L. - Todos los derechos reservados. Prohibida su reproducci?n
5      * 
6      * http://www.planetalia.com
7      * 
8      */
9     package version18;
10    
11    import java.awt.image.ImageObserver;
12    
13    public interface Stage extends ImageObserver {
14      public static final int WIDTH=640;
15      public static final int HEIGHT=480;
16      public static final int SPEED=10;
17      public SpriteCache getSpriteCache();
18 public void addActor(Actor a);
19 } 20

Removing actors, however, is not as simple as merely creating a new method removeActor(...) in Stage. The reason is that this removal method might get called from a differen thread - for example as a result of a keyboard event. But it could happen that at the same time we are trying to remove an actor, the main game loop (and the main thread) are iterating the list of actors doing something. Removing an actor in the middle of the iteration - without these loops knowing it could cause problems, like for example the act() method of the actor following the removed one not being called, or calling the act() method of the actor we've just removed, or painting it again even though it has just been removed

One possible solution is to synchronize all accesses to the global list of actors, but we would be creating a big bottleneck and we want to keep the game as fast as possible

A second option is creating a tag inside the Actor class that acts as a "marker for removal". Whenever someone in any thread wants to remove an actor, it simply "marks" it "for removal". The main loop can afterwards check this mark and remove the actor from the list of actors without creating concurrency problems :

This new concept requires a rewrite of the Actor class in the following way:


1     /**
2      * Curso B?sico de desarrollo de Juegos en Java - Invaders
3      * 
4      * (c) 2004 Planetalia S.L. - Todos los derechos reservados. Prohibida su reproducci?n
5      * 
6      * http://www.planetalia.com
7      * 
8      */
9     package version18;
10    
11    import java.awt.Graphics2D;
12    import java.awt.image.BufferedImage;
13    
14    public class Actor {
15      protected int x,y;
16      protected int width, height;
17      protected String[] spriteNames;
18      protected int currentFrame;
19      protected int frameSpeed;
20      protected int t;
21      protected Stage stage;
22      protected SpriteCache spriteCache;
23 protected boolean markedForRemoval;
24 25 public Actor(Stage stage) { 26 this.stage = stage; 27 spriteCache = stage.getSpriteCache(); 28 currentFrame = 0; 29 frameSpeed = 1; 30 t=0; 31 } 32
33 public void remove() { 34 markedForRemoval = true; 35 } 36 37 public boolean isMarkedForRemoval() { 38 return markedForRemoval; 39 } 40
41 public void paint(Graphics2D g){ 42 g.drawImage( spriteCache.getSprite(spriteNames[currentFrame]), x,y, stage ); 43 } 44 45 public int getX() { return x; } 46 public void setX(int i) { x = i; } 47 48 public int getY() { return y; } 49 public void setY(int i) { y = i; } 50 51 public int getFrameSpeed() {return frameSpeed; } 52 public void setFrameSpeed(int i) {frameSpeed = i; } 53 54 55 public void setSpriteNames(String[] names) { 56 spriteNames = names; 57 height = 0; 58 width = 0; 59 for (int i = 0; i < names.length; i++ ) { 60 BufferedImage image = spriteCache.getSprite(spriteNames[i]); 61 height = Math.max(height,image.getHeight()); 62 width = Math.max(width,image.getWidth()); 63 } 64 } 65 66 public int getHeight() { return height; } 67 public int getWidth() { return width; } 68 public void setHeight(int i) {height = i; } 69 public void setWidth(int i) { width = i; } 70 71 public void act() { 72 t++; 73 if (t % frameSpeed == 0){ 74 t=0; 75 currentFrame = (currentFrame + 1) % spriteNames.length; 76 } 77 } 78 } 79

And now the methods updateWorld() and addActor() become :


           . . .  
75      public void addActor(Actor a) {
76        actors.add(a);
77      } 
78      
79      public void updateWorld() {
80        int i = 0;
81        while (i < actors.size()) {
82          Actor m = (Actor)actors.get(i);
83          if (m.isMarkedForRemoval()) {
84            actors.remove(i);
85          } else {
86            m.act();
87            i++;
88          }
89        }
90        player.act();
91      }
           . . .  

Once that we have a robust system for removing actors, we can handle the missiles. For a missile we'll use the following image, called "disparo.gif":

Curso de Java

Missiles, as they have their own behaviour, will need a new class to represent them. We'll call that class Bullet:


1     /**
2      * Curso B?sico de desarrollo de Juegos en Java - Invaders
3      * 
4      * (c) 2004 Planetalia S.L. - Todos los derechos reservados. Prohibida su reproducci?n
5      * 
6      * http://www.planetalia.com
7      * 
8      */
9     package version18;
10    
11    
12    public class Bullet extends Actor {
13      protected static final int BULLET_SPEED=10;
14      
15      public Bullet(Stage stage) {
16        super(stage);
17        setSpriteNames( new String[] {"misil.gif"});
18      }
19      
20      public void act() {
21        super.act();
22        y-=BULLET_SPEED;
23        if (y < 0)
24          remove();
25      }
26    }
27    

The act() method of a missile moves it upwards and removes it (or more correctly, marks it for removal) whenever it reaches the upper limit of the screen

Finally, the only thing left is adding a new key to the Player class that will fire a missile when pressed:


1     /**
2      * Curso B?sico de desarrollo de Juegos en Java - Invaders
3      * 
4      * (c) 2004 Planetalia S.L. - Todos los derechos reservados. Prohibida su reproducci?n
5      * 
6      * http://www.planetalia.com
7      * 
8      */
9     package version18;
10    
11    import java.awt.event.KeyEvent;
12    
13    public class Player extends Actor {
14      protected static final int PLAYER_SPEED = 4;
15      protected int vx;
16      protected int vy;
17      private boolean up,down,left,right;
18        
19      
20      public Player(Stage stage) {
21        super(stage);
22        setSpriteNames( new String[] {"nave.gif"});
23      }
24      
25      public void act() {
26        super.act();
27        x+=vx;
28        y+=vy;
29        if (x < 0 || x > Stage.WIDTH)
30          vx = -vx;
31        if (y < 0 || y > Stage.HEIGHT)
32          vy = -vy;
33      }
34    
35      public int getVx() { return vx; }
36      public void setVx(int i) {vx = i; }
37      public int getVy() { return vy; }
38      public void setVy(int i) {vy = i; }
39      
40      
41      protected void updateSpeed() {
42        vx=0;vy=0;
43        if (down) vy = PLAYER_SPEED;
44        if (up) vy = -PLAYER_SPEED;
45        if (left) vx = -PLAYER_SPEED;
46        if (right) vx = PLAYER_SPEED;
47      }
48      
49      public void keyReleased(KeyEvent e) {
50        switch (e.getKeyCode()) {
51          case KeyEvent.VK_DOWN : down = false;break;
52          case KeyEvent.VK_UP : up = false; break;
53          case KeyEvent.VK_LEFT : left = false; break; 
54          case KeyEvent.VK_RIGHT : right = false;break;
55        }
56        updateSpeed();
57      }
58      
59      public void keyPressed(KeyEvent e) {
60        switch (e.getKeyCode()) {
61          case KeyEvent.VK_UP : up = true; break;
62          case KeyEvent.VK_LEFT : left = true; break;
63          case KeyEvent.VK_RIGHT : right = true; break;
64          case KeyEvent.VK_DOWN : down = true;break;
65 case KeyEvent.VK_SPACE : fire(); break;
66 } 67 updateSpeed(); 68 } 69
70 public void fire() { 71 Bullet b = new Bullet(stage); 72 b.setX(x); 73 b.setY(y - b.getHeight()); 74 stage.addActor(b); 75 }
76 77 } 78

Curso de Java



Do you want to be notified when new tutorials or lessons are published? Press here


Full list of Java source files for this step

Bullet.java Stage.java Monster.java SpriteCache.java
Invaders.java Actor.java Player.java  

Full list of resources

explosion.wav disparo2.gif bombUL.gif oceano.gif
bombDR.gif disparo0.gif bicho0.gif bombD.gif
missile.wav test.gif disparo1.gif bombU.gif
bombDL.gif bombL.gif bicho.gif Thumbs.db
bombR.gif bicho2.gif bicho1.gif disparo.gif
musica.wav bombUR.gif photon.wav misil.gif
nave.gif      

  Previous Page - Controlling the player Current Page - 18 - Shots   Next Page - More shooting - Cluster bombs
  Return to the index of free Java tutorials  

(c) 2004 Planetalia S.L. All rights reserved. Unauthorized reproduction and/or mirroring is not permitted