Introduciendo un misil en Asteroides

Para poder disparar a los asteroides va ha ser necesario introducir un misil en el juego. En el siguiente ejercicio aprenderemos a hacerlo:

Ejercicio paso a paso: Introduciendo un misil en Asteroides

1. En primer lugar añade las siguientes variables a la clase VistaJuego:

// //// MISIL //////

private Grafico misil;

private static int PASO_VELOCIDAD_MISIL = 12;

private boolean misilActivo = false;

private int tiempoMisil;

2. Para trabajar con gráficos vectoriales, puedes crear en el constructor la variable drawableMisil de la siguiente forma:

ShapeDrawable dMisil = new ShapeDrawable(new RectShape());

dMisil.getPaint().setColor(Color.WHITE);

dMisil.getPaint().setStyle(Style.STROKE);

dMisil.setIntrinsicWidth(15);

dMisil.setIntrinsicHeight(3);

drawableMisil = dMisil;

3. Crea la variable drawableMisil para el caso de que se deseen gráficos en bitmap, utilizando el fichero misil1.png.

4. Inicializa el objeto misil. de forma similar a como se ha hecho en nave.

5. En el método onDraw() dibuja misil, solo si lo indica la variable misilActivo.

6. Quita los comentarios de las llamadas a activaMisil()

7. En el método actualizaFisica() añade las siguientes líneas:

 

// Actualizamos posición de misil

if (misilActivo) {

       misil.incrementaPos(retardo);

       tiempoMisil-=retardo;

       if (tiempoMisil < 0) {

             misilActivo = false;

       } else {

for (int i = 0; i < Asteroides.size(); i++)

             if (misil.verificaColision(Asteroides.elementAt(i))) {

                    destruyeAsteroide(i);

                    break;

             }

       }

}

8. Añade los siguientes dos métodos:

private void destruyeAsteroide(int i) {

       Asteroides.remove(i);

       misilActivo = false;

}

 

private void ActivaMisil() {

     
       misil.setCenX(nave.getCenX();

       misil.setCenY(nave.getCenY();

       misil.setAngulo(nave.getAngulo());

       misil.setIncX(Math.cos(Math.toRadians(misil.getAngulo())) * 
                                                                                             
PASO_VELOCIDAD_MISIL);

       misil.setIncY(Math.sin(Math.toRadians(misil.getAngulo())) * 
                                                                                             
PASO_VELOCIDAD_MISIL);

       tiempoMisil = (int) Math.min(this.getWidth() / Math.abs( misil.                 getIncX()), this.getHeight() / Math.abs(misil.getIncY())) - 2;

       misilActivo = true;

}

Este último método requiere alguna explicación. Cuando se quiere activar un nuevo misil este ha de partir del centro de la nave. Como las coordenadas X, Y de Grafico, corresponden a la esquina superior izquierda hay que hacer algunos ajustes. El ángulo del misil ha de ser el mismo que actualmente tiene la nave. El módulo de la velocidad de la nave nos la indica la constante PASO_VELOCIDAD_MISIL. Para descomponerla en sus componentes X e Y utilizamos el coseno y el seno. Dada la naturaleza del espacio del juego (lo que sale por un lado aparece por el otro) si disparáramos un misil este podría acabar chocando contra la nave. Para solucionarlo vamos a dar un tiempo de vida al misil para impedir que pueda llegar de nuevo a la nave (tiempoMisil). Para obtener este tiempo nos quedamos con el mínimo entre el ancho dividido la velocidad en X y el alto dividido entre la velocidad en Y. Luego le restamos una constante. Terminamos activando el misil.

9.verifica que todo funciona correctamente.

NOTA: Este código es posible que de algún problema de acceso concurrente a los datos desde dos threads diferentes. Se resolverá en el siguiente capítulo.

Práctica: Disparando varios misiles a la vez

Tal y como se ha planteado el código solo es posible lanzar un misil cada vez. Si disparamos un segundo misil el primero desaparece.  ¿Podrías modificar el código para que se pudieran lanzar tantos misiles cómo quisieras? Si no tienes muy claro por donde empezar, a continuación se plantean los pasos para una posible solución:

1. Elimina la variable misil, y en su lugar crea una vector de gráficos:

private Vector <Grafico> Misiles;

2. Elimina la variable misilActivo. Cuando el array de misiles está vacío querá decir que no hay ningún misil activo.

3. Elimina la variable tiempoMisil, y en su lugar crea una vector de enteros:

private Vector <Integer> tiempoMisiles;

Los elementos de los arrays Misiles y tiempoMisiles han de estar emparejados. Es decir, al misil en posición x de Misiles le quedará un tiempo que se almacenará en la posición x de tiempoMisiles.

Nota sobre Java: Observa como la variable tiempoMisil antes era de tipo int, pero ahora tiempoMisiles es en vertor de Integer, no de int. Estos dos tipos de datos representan un número entero, pero el primero es una clase y el segundo un tipo simple. Hemos tenido que realizar este cambio dado que la clase Vector solo admite elementos que sean clases. Para más información consultar Apendice A: Referencia Java, Envolventes (wrappers).

4. En los métodos onDraw() y antualizaFisica() tendrás que añadir un bucle para recorrer todos los misiles.

5. Observa la siguiente línea. Permite decrementar el elemento m de tiempoMisil

 

tiempoMisiles.set(m, tiempoMisiles.get(m)-1);

 

{jcomments on}