Planetalia - Java Training

Training and Consulting

Home

Tutorial - Writing a Space Invaders game in Java

  Previous Page - More shooting - Cluster bombs Current Page - 20 - Collision detection   Next Page - Status
  Return to the index of free Java tutorials  

Collision detection

(c) Alexander Hristov

So far our critters have been laughing in our face because of our inability to kill them regardless of our powerful list of weapons. It's about time to add collision detection to our program. Collision detection can be made as hard or as easy as one wishes. The standard way in 2D games to perform collision detection is to check if the bounding rectangles of the two objects intersect (overlap):

In order to maintain maximum flexibility for the future, we want to check if any two actors collide. This requires handling the global list of actors, and as such, must be done by the Invaders class, since the global list is its responsability

Fortunately, all Java classes that implement Shape (and Rectangle is one of them) include an intersects() method that allows us to check if the shape intersects with a given rectangle. Since we will be checking for collisions all actors, this means that we will need periodically to obtain the bounding rectangle of any of them. The best approach would be to code a method (for example getBounds()) in the Actor class.


           . . .  
79      
80      public Rectangle getBounds() {
81        return new Rectangle(x,y,width,height);
82      }
83      
           . . .  

Ok, so now we know how to detect a collision. And then what happens? Again we will make use of polymorphism and a very common pattern called responsability delegation : whenver a collision between two actors is detected, each of them is notified and each acts in whatever way it chooses. This means that there will be a new method in the Actor class called collision(...). This method will be called whenever the affected actor collides with something, and the main class will pass as a parameter the actor with which it has collided. By default this method will do nothing, and those who extend the base class will override it to suit their needs :



           . . .  
79      
80      public Rectangle getBounds() {
81        return new Rectangle(x,y,width,height);
82      }
83      
84      public void collision(Actor a){
85        
86      }
87    }
88    

Of course we would like the critters to die whenever they are hit by a missile or bomb. This will be handled in the newly defined method: Monster:


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 version20;
10    
11    public class Monster extends Actor {
12      protected int vx;
13      
14      public Monster(Stage stage) {
15        super(stage);
16        setSpriteNames( new String[] {"bicho0.gif","bicho1.gif"});
17        setFrameSpeed(35);
18      }
19      
20      public void act() {
21        super.act();
22        x+=vx;
23        if (x < 0 || x > Stage.WIDTH)
24          vx = -vx;
25      }
26    
27      public int getVx() { return vx; }
28      public void setVx(int i) {vx = i; }
29      
30 public void collision(Actor a) { 31 if (a instanceof Bullet || a instanceof Bomb) 32 remove(); 33 }
34 } 35

And finally we must code the loops in the main class that will check whether any collisions have occurred. The new method that will perform these checks will be checkCollisions():


1     package version20;
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.Graphics2D;
16    import java.awt.Rectangle;
17    import java.awt.event.KeyEvent;
18    import java.awt.event.KeyListener;
19    import java.awt.event.WindowAdapter;
20    import java.awt.event.WindowEvent;
21    import java.awt.image.BufferStrategy;
22    import java.util.ArrayList;
23    
24    import javax.swing.JFrame;
25    import javax.swing.JPanel;
26    
27    public class Invaders extends Canvas implements Stage, KeyListener {
28      
29      private BufferStrategy strategy;
30      private long usedTime;
31      
32      private SpriteCache spriteCache;
33      private ArrayList actors; 
34      private Player player;
35      
36      public Invaders() {
37        spriteCache = new SpriteCache();
38        
39      
40        JFrame ventana = new JFrame("Invaders");
41        JPanel panel = (JPanel)ventana.getContentPane();
42        setBounds(0,0,Stage.WIDTH,Stage.HEIGHT);
43        panel.setPreferredSize(new Dimension(Stage.WIDTH,Stage.HEIGHT));
44        panel.setLayout(null);
45        panel.add(this);
46        ventana.setBounds(0,0,Stage.WIDTH,Stage.HEIGHT);
47        ventana.setVisible(true);
48        ventana.addWindowListener( new WindowAdapter() {
49          public void windowClosing(WindowEvent e) {
50            System.exit(0);
51          }
52        });
53        ventana.setResizable(false);
54        createBufferStrategy(2);
55        strategy = getBufferStrategy();
56        requestFocus();
57        addKeyListener(this);
58      }
59      
60      public void initWorld() {
61        actors = new ArrayList();
62        for (int i = 0; i < 10; i++){
63          Monster m = new Monster(this);
64          m.setX( (int)(Math.random()*Stage.WIDTH) );
65          m.setY( i*20 );
66          m.setVx( (int)(Math.random()*20-10) );
67          
68          actors.add(m);
69        }
70        
71        player = new Player(this);
72        player.setX(Stage.WIDTH/2);
73        player.setY(Stage.HEIGHT - 2*player.getHeight());
74      }
75      
76      public void addActor(Actor a) {
77        actors.add(a);
78      } 
79      
80      public void updateWorld() {
81        int i = 0;
82        while (i < actors.size()) {
83          Actor m = (Actor)actors.get(i);
84          if (m.isMarkedForRemoval()) {
85            actors.remove(i);
86          } else {
87            m.act();
88            i++;
89          }
90        }
91        player.act();
92      }
93      
94 public void checkCollisions() { 95 Rectangle playerBounds = player.getBounds(); 96 for (int i = 0; i < actors.size(); i++) { 97 Actor a1 = (Actor)actors.get(i); 98 Rectangle r1 = a1.getBounds(); 99 if (r1.intersects(playerBounds)) { 100 player.collision(a1); 101 a1.collision(player); 102 } 103 for (int j = i+1; j < actors.size(); j++) { 104 Actor a2 = (Actor)actors.get(j); 105 Rectangle r2 = a2.getBounds(); 106 if (r1.intersects(r2)) { 107 a1.collision(a2); 108 a2.collision(a1); 109 } 110 } 111 } 112 }
113 114 public void paintWorld() { 115 Graphics2D g = (Graphics2D)strategy.getDrawGraphics(); 116 g.setColor(Color.black); 117 g.fillRect(0,0,getWidth(),getHeight()); 118 for (int i = 0; i < actors.size(); i++) { 119 Actor m = (Actor)actors.get(i); 120 m.paint(g); 121 } 122 player.paint(g); 123 124 g.setColor(Color.white); 125 if (usedTime > 0) 126 g.drawString(String.valueOf(1000/usedTime)+" fps",0,Stage.HEIGHT-50); 127 else 128 g.drawString("--- fps",0,Stage.HEIGHT-50); 129 strategy.show(); 130 } 131 132 public SpriteCache getSpriteCache() { 133 return spriteCache; 134 } 135 136 public void keyPressed(KeyEvent e) { 137 player.keyPressed(e); 138 } 139 140 public void keyReleased(KeyEvent e) { 141 player.keyReleased(e); 142 } 143 public void keyTyped(KeyEvent e) {} 144 145 public void game() { 146 usedTime=1000; 147 initWorld(); 148 while (isVisible()) { 149 long startTime = System.currentTimeMillis(); 150 updateWorld();
151 checkCollisions();
152 paintWorld(); 153 usedTime = System.currentTimeMillis()-startTime; 154 try { 155 Thread.sleep(SPEED); 156 } catch (InterruptedException e) {} 157 } 158 } 159 160 public static void main(String[] args) { 161 Invaders inv = new Invaders(); 162 inv.game(); 163 } 164 } 165


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
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 - More shooting - Cluster bombs Current Page - 20 - Collision detection   Next Page - Status
  Return to the index of free Java tutorials  

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