Planetalia - Java Training

Training and Consulting

Home

Tutorial - Writing a Space Invaders game in Java

  Previous Page - The revenge of the Monsters Current Page - 25 - A Scrolling playfield   Next Page - Monster respawning
  Return to the index of free Java tutorials  

A Scrolling playfield

(c) Alexander Hristov

Many shooters achieve a very nice effect by adding a scrolling background. This effect is very easy to achieve simply by erasing the screen with a texture instead of a solid black color. The scrolling effect is achieved by changing the coordinates of the anchor rectangle. To understand this, imagine that we have the following image, which we'll use as a texture:

If we fill the screen with it using an anchor rectangle of (0,0,256,256), we get the following effect:

Now if we use an anchor rectangle of (0,20,256,256), that is, if we increase the y coord of the upper left corner of the rectangle, we get the following :

The effect is as if the whole background had moved 20 points downwards. Precisely this is what we'll be using to achieve a scrolling background. Of course, there are quicker ways to achieve the same effect, but we'll use this as a starting point. We also want a smoother movement, so we will be increasing the position of the anchor rectangle one pixel per turn. And of course we will use an image better suited for a background, like the following one, called "oceano.gif":

The only class that is changed in this step is the Invaders class, and it becomes the following:


1     package version25;
2     /**
3      * Curso B?sico de desarrollo de Juegos en Java - Invaders
4      * 
5      * (c) 2004 Planetalia S.L. - Todos los derechos reservados. Prohibida su reproducci?n
6      * 
7      * http://www.planetalia.com
8      * 
9      */
10    
11    
12    import java.awt.Canvas;
13    import java.awt.Color;
14    import java.awt.Dimension;
15    import java.awt.Font;
16    import java.awt.Graphics2D;
17    import java.awt.Rectangle;
18    import java.awt.TexturePaint;
19    import java.awt.event.KeyEvent;
20    import java.awt.event.KeyListener;
21    import java.awt.event.WindowAdapter;
22    import java.awt.event.WindowEvent;
23    import java.awt.image.BufferStrategy;
24    import java.awt.image.BufferedImage;
25    import java.util.ArrayList;
26    
27    import javax.swing.JFrame;
28    import javax.swing.JPanel;
29    
30    public class Invaders extends Canvas implements Stage, KeyListener {
31      
32      private BufferStrategy strategy;
33      private long usedTime;
34      
35      private SpriteCache spriteCache;
36      private ArrayList actors; 
37      private Player player;
38 private BufferedImage ocean; 39 private int t;
40 41 private boolean gameEnded=false; 42 43 public Invaders() { 44 spriteCache = new SpriteCache(); 45 46 47 JFrame ventana = new JFrame("Invaders"); 48 JPanel panel = (JPanel)ventana.getContentPane(); 49 setBounds(0,0,Stage.WIDTH,Stage.HEIGHT); 50 panel.setPreferredSize(new Dimension(Stage.WIDTH,Stage.HEIGHT)); 51 panel.setLayout(null); 52 panel.add(this); 53 ventana.setBounds(0,0,Stage.WIDTH,Stage.HEIGHT); 54 ventana.setVisible(true); 55 ventana.addWindowListener( new WindowAdapter() { 56 public void windowClosing(WindowEvent e) { 57 System.exit(0); 58 } 59 }); 60 ventana.setResizable(false); 61 createBufferStrategy(2); 62 strategy = getBufferStrategy(); 63 requestFocus(); 64 addKeyListener(this); 65 } 66 67 public void gameOver() { 68 gameEnded = true; 69 } 70 71 public void initWorld() { 72 actors = new ArrayList(); 73 for (int i = 0; i < 10; i++){ 74 Monster m = new Monster(this); 75 m.setX( (int)(Math.random()*Stage.WIDTH) ); 76 m.setY( i*20 ); 77 m.setVx( (int)(Math.random()*20-10) ); 78 79 actors.add(m); 80 } 81 82 player = new Player(this); 83 player.setX(Stage.WIDTH/2); 84 player.setY(Stage.PLAY_HEIGHT - 2*player.getHeight()); 85 } 86 87 public void addActor(Actor a) { 88 actors.add(a); 89 } 90 91 public Player getPlayer() { 92 return player; 93 } 94 95 public void updateWorld() { 96 int i = 0; 97 while (i < actors.size()) { 98 Actor m = (Actor)actors.get(i); 99 if (m.isMarkedForRemoval()) { 100 actors.remove(i); 101 } else { 102 m.act(); 103 i++; 104 } 105 } 106 player.act(); 107 } 108 109 public void checkCollisions() { 110 Rectangle playerBounds = player.getBounds(); 111 for (int i = 0; i < actors.size(); i++) { 112 Actor a1 = (Actor)actors.get(i); 113 Rectangle r1 = a1.getBounds(); 114 if (r1.intersects(playerBounds)) { 115 player.collision(a1); 116 a1.collision(player); 117 } 118 for (int j = i+1; j < actors.size(); j++) { 119 Actor a2 = (Actor)actors.get(j); 120 Rectangle r2 = a2.getBounds(); 121 if (r1.intersects(r2)) { 122 a1.collision(a2); 123 a2.collision(a1); 124 } 125 } 126 } 127 } 128 129 public void paintShields(Graphics2D g) { 130 g.setPaint(Color.red); 131 g.fillRect(280,Stage.PLAY_HEIGHT,Player.MAX_SHIELDS,30); 132 g.setPaint(Color.blue); 133 g.fillRect(280+Player.MAX_SHIELDS-player.getShields(),Stage.PLAY_HEIGHT,player.getShields(),30); 134 g.setFont(new Font("Arial",Font.BOLD,20)); 135 g.setPaint(Color.green); 136 g.drawString("Shields",170,Stage.PLAY_HEIGHT+20); 137 138 } 139 140 public void paintScore(Graphics2D g) { 141 g.setFont(new Font("Arial",Font.BOLD,20)); 142 g.setPaint(Color.green); 143 g.drawString("Score:",20,Stage.PLAY_HEIGHT + 20); 144 g.setPaint(Color.red); 145 g.drawString(player.getScore()+"",100,Stage.PLAY_HEIGHT + 20); 146 } 147 148 public void paintAmmo(Graphics2D g) { 149 int xBase = 280+Player.MAX_SHIELDS+10; 150 for (int i = 0; i < player.getClusterBombs();i++) { 151 BufferedImage bomb = spriteCache.getSprite("bombUL.gif"); 152 g.drawImage( bomb ,xBase+i*bomb.getWidth(),Stage.PLAY_HEIGHT,this); 153 } 154 } 155 156 public void paintfps(Graphics2D g) { 157 g.setFont( new Font("Arial",Font.BOLD,12)); 158 g.setColor(Color.white); 159 if (usedTime > 0) 160 g.drawString(String.valueOf(1000/usedTime)+" fps",Stage.WIDTH-50,Stage.PLAY_HEIGHT); 161 else 162 g.drawString("--- fps",Stage.WIDTH-50,Stage.PLAY_HEIGHT); 163 } 164 165 166 public void paintStatus(Graphics2D g) { 167 paintScore(g); 168 paintShields(g); 169 paintAmmo(g); 170 paintfps(g); 171 } 172 173 public void paintWorld() { 174 Graphics2D g = (Graphics2D)strategy.getDrawGraphics();
175 ocean = spriteCache.getSprite("oceano.gif"); 176 g.setPaint(new TexturePaint(ocean, new Rectangle(0,t,ocean.getWidth(),ocean.getHeight()))); 177 g.fillRect(0,0,getWidth(),getHeight());
178 for (int i = 0; i < actors.size(); i++) { 179 Actor m = (Actor)actors.get(i); 180 m.paint(g); 181 } 182 player.paint(g); 183 184 185 paintStatus(g); 186 strategy.show(); 187 } 188 189 public void paintGameOver() { 190 Graphics2D g = (Graphics2D)strategy.getDrawGraphics(); 191 g.setColor(Color.white); 192 g.setFont(new Font("Arial",Font.BOLD,20)); 193 g.drawString("GAME OVER",Stage.WIDTH/2-50,Stage.HEIGHT/2); 194 strategy.show(); 195 } 196 197 public SpriteCache getSpriteCache() { 198 return spriteCache; 199 } 200 201 public void keyPressed(KeyEvent e) { 202 player.keyPressed(e); 203 } 204 205 public void keyReleased(KeyEvent e) { 206 player.keyReleased(e); 207 } 208 public void keyTyped(KeyEvent e) {} 209 210 public void game() { 211 usedTime=1000;
212 t = 0;
213 initWorld(); 214 while (isVisible() && !gameEnded) {
215 t++;
216 long startTime = System.currentTimeMillis(); 217 updateWorld(); 218 checkCollisions(); 219 paintWorld(); 220 usedTime = System.currentTimeMillis()-startTime; 221 try { 222 Thread.sleep(SPEED); 223 } catch (InterruptedException e) {} 224 } 225 paintGameOver(); 226 } 227 228 public static void main(String[] args) { 229 Invaders inv = new Invaders(); 230 inv.game(); 231 } 232 } 233

The effect we get is the following:

and our game has now a nice looking scrolling background



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


Full list of Java source files for this step

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

Full list of resources

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

  Previous Page - The revenge of the Monsters Current Page - 25 - A Scrolling playfield   Next Page - Monster respawning
  Return to the index of free Java tutorials  

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