Planetalia - Cursos

Cursos y Másters

Inicio    Profesores    Aulas    Testimonios    Cursos    Masters    Tecnologías    Cursos Gratuitos    Solicitar Información    Ofertas de Empleo   

Curso - Programación de un Space Invaders en Java

  Página Anterior - Controlando el jugador Página Actual - 18 - Disparos   Página Siguiente - Más disparos - Bombas de fragmentación
  Índice de cursos Java gratuitos  

Disparos

(c) Alexander Hristov

Ha llegado el momento de añadir algo de violencia a nuestro pacífico juego - la posibilidad de que el jugador dispare. Nos conformaremos inicialmente con poder disparar y de momento dejaremos el efecto que los disparos producen sobre el entorno (en particular los monstruos) para más adelante.

Nuestros disparos tendrán las siguientes premisas:

  1. El disparo aparece justo encima de la nave del jugador
  2. Los disparos se mueven hacia arriba, siempre a la misma velocidad
  3. El jugador sólo puede disparar cinco misiles cada vez

Desde el punto de vista del programa, los disparos son simplemente actores. Lo novedoso es que los disparos aparecen y desaparecen sobre la marcha y dependiendo de circunstancias que el bucle principal del programa no controla. Por ejemplo, cuando se pulsa la barra espaciadora, aparece un disparo. Este actor debe ser añadido a la lista, pero la gestión de las teclas la hace la clase Player, y no sería correcto que esta clase "metiera la mano" dentro de la lista de actores, ya que esta lista es una estructura de datos propia de la clase Invaders.

Una solución es dotar al escenario de métodos que permitan añadir y quitar actores sobre la marcha. Con esta idea, la interfaz Stage quedaría de la siguiente forma:


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

Ahora bien, hay un problema importante que no es obvio a primera vista : ¿quién borra los actores? Lo primero que se nos podría ocurrir es añadir un método removeActor(...) igual que el otro. Pero surge un problema y es que es posible que el borrado de un actor se haga como respuesta a la pulsación de una tecla, o bien como consecuencia de un método act() de otro actor. En el primer caso, el borrado se estaría produciendo desde un hilo distinto al principal (nada bueno ya que estaríamos manipulando una estructura de datos desde un hilo, mientras que desde el principal tal vez la estemos recorriendo). En el segundo caso, estaríamos en una situación similar - podría producirse un desajuste en el bucle y que el método act() de algún actor no fuese llamado

Para evitar ambas situaciones, en lugar de borrar un actor directamente definiremos una marca que indique que un actor determinado está "marcado para ser borrado". Posteriormente será el bucle principal el único que comprobará dicha marca y eliminará el actor si procede:

Esto requiere una modificación a la clase Actor de la siguiente forma:


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

Los métodos updateWorld() y addActor() de la clase Invaders.java quedarían de la siguiente forma:


           . . .  
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      }
           . . .  

Una vez que hemos resuelto los problemas de adición y eliminación de actores, podemos comenzar a añadir la gestión de los disparos. Para el disparo utilizaremos el siguiente gráfico, llamado "disparo.gif":

Curso de Java

Los disparos, al tener su propio comportamiento, van a estar representados por una nueva clase llamada 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    

Se puede ver que el metodo act() borra automáticamente el disparo cuando detecta que ha salido por la parte superior de la pantalla

Por último, queda añadir la gestión de la tecla de disparo a la clase Player:


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



¿Quieres ser notificado cuando salan nuevos tutoriales y cursos?. Pulsa aquí


Lista de archivos Java del programa en este paso

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

Lista de recursos

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      

  Página Anterior - Controlando el jugador Página Actual - 18 - Disparos   Página Siguiente - Más disparos - Bombas de fragmentación
  Índice de cursos Java gratuitos  

(c) 2004 Planetalia S.L. Todos los derechos reservados. Prohibida su reproducción