Una vez que conocemos los rudimentos que nos permiten utilizar gráficos en Android vamos a aplicarlos a nuestro ejemplo.

Ejercicio paso a paso: Creando la actividad principal en Asteroides

Lo primero que necesitamos es crear una actividad que controle la pantalla del juego propiamente dicho. A esta actividad le llamaremos Juego.

1.     Abre el proyecto Asteroides.

2.     Lo primero que necesitamos es crear una actividad que controle la pantalla del juego propiamente dicho. Crea la clase Juego con el siguiente código.

public class Juego extends Activity {
   @Override public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.juego);
   }
}

3.     Necesitaremos un Layout para la pantalla del juego. Crea el fichero res/layout/juego.xml con el siguiente contenido:

<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    <org.example.asteroides.VistaJuego
          android:id="@+id/VistaJuego"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:focusable="true"
          android:background="@drawable/fondo" />
</LinearLayout>

Como puedes observar, cuando diseñamos un Layout en XML, no estamos limitados a escoger los elementos que tenemos en la paleta de vistas; vamos a utilizar vistas creadas por nosotros. En este ejemplo utilizaremos la vista org.example.asteroides.VistaJuego que será descrita más adelante. Va a ser esta vista la que lleve el peso de la aplicación

4.   Observa que se ha indicado el parámetro android:background asociado al recurso @drawable/fondo. Por lo tanto, tendremos que poner el recurso fondo.jpg en la carpeta correspondiente. Copia también los gráficos correspondientes a los asteroides, la nave y el misil a la carpeta res/drawable. Estos gráficos serán utilizados en los siguientes apartados. Los puedes encontrar en www.androidcurso.com.

5.  De momento no podremos ejecutar la aplicación hasta haber definido la clase VistaJuego.

La clase Gráfico

El juego que estamos desarrollando va a desplazar muchos tipos de gráficos por pantalla: asteroides, nave, misiles,… Dado que el comportamiento de todos estos elementos es muy similar, con el fin de reutilizar el código y mejorar su comprensión, vamos a crear una clase que represente un gráfico a desplazar por pantalla. A esta nueva clase le llamaremos Grafico y presentará las siguientes características. El elemento a dibujar será representado en un objeto Drawable. Como hemos visto, esta clase presenta una gran versatilidad, lo que nos permitirá trabajar con gráficos en bitmap (BitmapDrawable), vectoriales (ShapeDrawable), gráficos con diferentes representaciones (StateListDrawable), gráficos animados (AnimationDrawable),… Además un Grafico dispondrá de posición, velocidad de desplazamiento, ángulo de rotación y velocidad de rotación. Para finalizar, un gráfico que salga por uno de los extremos de la pantalla reaparecerá por el extremo contrario, tal y como ocurría en el juego original de Asteroides. 

Ejercicio paso a paso: La clase Gráfico

1. Crea una nueva clase Graficoen el proyecto Asteroides y copia el siguiente código

class Grafico {

      private Drawable drawable;   //Imagen que dibujaremos

      private double posX, posY;   //Posición

      private double incX, incY;   //Velocidad desplazamiento

      private int angulo, rotacion;//Ángulo y velocidad rotación

      private int ancho, alto;     //Dimensiones de la imagen

      private int radioColision;   //Para determinar colisión

       //Donde dibujamos el gráfico (usada en view.ivalidate)

      private View view;

       // Para determinar el espacio a borrar (view.ivalidate)

      public static final int MAX_VELOCIDAD = 20;

     

      public Grafico(View view, Drawable drawable){

            this.view = view;

            this.drawable = drawable;

            ancho = drawable.getIntrinsicWidth();  

            alto = drawable.getIntrinsicHeight();

            radioColision = (alto+ancho)/4;

      }

           public void dibujaGrafico(Canvas canvas){

            canvas.save();

            int x=(int) (posX+ancho/2);

            int y=(int) (posY+alto/2);

            canvas.rotate((float) angulo,(float) x,(float) y);

            drawable.setBounds((int)posX, (int)posY,
                                                                 (int)posX+ancho, (int)posY+alto);

            drawable.draw(canvas);

            canvas.restore();

            int rInval = (int) Math.hypot(ancho,alto)/2 + MAX_VELOCIDAD;

            view.invalidate(x-rInval, y-rInval, x+rInval, y+rInval);

      }

      public void incrementaPos(double factor){

            posX+=incX * factor;

            // Si salimos de la pantalla, corregimos posición

            if(posX<-ancho/2) {posX=view.getWidth()-ancho/2;}

            if(posX>view.getWidth()-ancho/2) {posX=-ancho/2;}

            posY+=incY * factor;

            if(posY<-alto/2) {posY=view.getHeight()-alto/2;}

            if(posY>view.getHeight()-alto/2) {posY=-alto/2;}

            angulo += rotacion * factor; //Actualizamos ángulo

      }



      public double distancia(Grafico g) {

            return Math.hypot(posX-g.posX, posY-g.posY);

      }



      public boolean verificaColision(Grafico g) {

            return(distancia(g) < (radioColision+g.radioColision));

      }

}

2.    Al principio de la clase hemos definido varios campos con el modificador private. Vamos a necesitar acceder a estos campos desde fuera de la clase, por lo que resulta necesario declarar los métodos get y set correspondientes. Vamos a realizar esta tarea de forma automática utilizando una herramienta de Eclipse. Sitúa el cursor al final de la clase (justo antes de la última llave) y pulsa con el botón derecho. Selecciona en el menú desplegable Source/Generate Getters and Setters…En la ventana de diálogo marca todos los campos (botón Sellect All) ypula OK. Eclipse hará el trabajo por nosotros.

 Nota sobre Java:En el tutorial Java Esencial / Encapsulamiento y visibilidad puedes aprender más sobre los métodos get y set. Lo encontrarás en la Web www.androidcurso.com.

 

 

La clase VistaJuego

Pasemos a describir la creación de VistaJuego, que como hemos indicado es la responsable de la ejecución el juego. En una primera versión solo se representarán los asteroides de forma estática:

Ejercicio paso a paso: La clase VistaJuego

1. Crea una nueva clase VistaJuego en el proyecto Asteroides y copia el siguiente código:

public class VistaJuego extends View {

      // //// ASTEROIDES //////

      private Vector Asteroides; // Vector con los Asteroides

      private int numAsteroides= 5; // Número inicial de asteroides

      private int numFragmentos= 3; // Fragmentos en que se divide



      public VistaJuego(Context context, AttributeSet attrs) {

            super(context, attrs);

            Drawable drawableNave, drawableAsteroide, drawableMisil;

            drawableAsteroide = context.getResources().getDrawable(
                                                                                  R.drawable.asteroide1);

            Asteroides = new Vector();

            for (int i = 0; i < numAsteroides; i++) {

                  Grafico asteroide = new Grafico(this, drawableAsteroide);

                  asteroide.setIncY(Math.random() * 4 - 2);

                  asteroide.setIncX(Math.random() * 4 - 2);

                  asteroide.setAngulo((int) (Math.random() * 360));

                  asteroide.setRotacion((int) (Math.random() * 8 - 4));

                  Asteroides.add(asteroide);

            }

      }



      @Override protected void onSizeChanged(int ancho, int alto,
                                                           int ancho_anter, int alto_anter) {

            super.onSizeChanged(ancho, alto, ancho_anter, alto_anter);

            // Una vez que conocemos nuestro ancho y alto.

            for (Grafico asteroide: Asteroides) {

                  asteroide.setPosX(Math.random()*
                                                                       (ancho-asteroide.getAncho()));

                  asteroide.setPosY(Math.random()*
                                                                       (alto-asteroide.getAlto()));

            }

      }



      @Override protected void onDraw(Canvas canvas) {

            super.onDraw(canvas);

            for (Grafico asteroide: Asteroides) {

                asteroide.dibujaGrafico(canvas);

            }

      }

}

Como ves se han declarado tres métodos. En el constructor creamos los asteroides e inicializamos su velocidad, ángulo y rotación. Sin embargo, resulta imposible iniciar su posición, dado que no conocemos el alto y ancho de la pantalla. Esta información será conocida cuando se llame a onSizeChanged(). Observa cómo en esta función los asteroides están situados de forma aleatoria. El último método, onDraw(), es el más importante de la clase View, dado que es el responsable de dibujar la vista.

2. Hemos creado una vista personalizada. No tendría demasiado sentido, pero podrá ser utilizada en cualquier otra aplicación que desarrolles. Visualiza el Layout juego.xml y observa como la nueva vista se integra perfectamente en el entorno de desarrollo.

 

3. Registra la actividad Juego en AndroidManifest.xml.

4. Ejecuta la aplicación. Has de ver cinco asteroides repartidos al azar por la pantalla

 

Introduciendo la nave en VistaJuego

El siguiente paso consiste en dibujar la nave que controlará el usuario para destruir los asteroides.

 

Práctica: Introduciendo la nave en VistaJuego

1. Declara las siguientes variables al comienzo de la clase VistaJuego:

// //// NAVE //////

   private Grafico nave;// Gráfico de la nave

   private int giroNave; // Incremento de dirección

   private float aceleracionNave; // aumento de velocidad

   // Incremento estándar de giro y aceleración

   private static final int PASO_GIRO_NAVE = 5;

   private static final float PASO_ACELERACION_NAVE = 0.5f;

Algunas de estas variables serán utilizadas en el siguiente capítulo.

2. En el constructor de la clase instancia la variable drawableNave de forma similar como se ha hecho en drawableAsteroide.

3. Inicializa también en el constructor la variable navede la siguiente forma:

            nave = new Grafico(this, drawableNave);

4. En el método onSiceChange() posiciona la nave justo en el centro de la vista.

5. En el método onDraw() dibuja la nave en el Canvas.

6. Ejecuta la aplicación. La nave ha de aparecer centrada:

 

7. Si cuando situamos los asteroides, alguno coincide con la posición de la nave, el jugador no tendrá ninguna opción de sobrevivir. Sería más interesante asegurarnos de que al posicionar los asteroides estos se encuentran a una distancia adecuada a la nave, y en caso contrario tratar de obtener otra posición. Para conseguirlo puedes utilizar el siguiente código en sustitución de las dos líneas asteroide.setPosX(…) y asteroide.setPosY(…).

do{

      asteroide.setPosX(Math.random()*(ancho-asteroide.getAncho()));

      asteroide.setPosY(Math.random()*(alto-asteroide.getAlto()));

} while(asteroide.distancia(nave) < (ancho+alto)/5);

Ejercicio paso a paso: Evitando que VistaJuego cambie su representación con el dispositivo en horizontal y en vertical

 

 

1. Ejecuta la aplicación

2. Cambia de orientación la pantalla del dispositivo. En el emulador se consigue pulsando la tecla Ctrl-F11.

3. Observa cómo cada vez, se reinicializa la vista, regenerando los asteroides. Esto nos impediría jugar de forma adecuada. Para solucionarlo edita AndroidManifet.xml. En la lengüeta Application selecciona la actividad Juego. En los parámetros de la derecha selecciona en Screen orientation: landscape.

4. Ejecuta de nuevo la aplicación. Observa como la actividad Juego será siempre representada en modo horizontal, de forma independiente a la posición del teléfono.

5. Abre de nuevo las propiedades de la actividad Juego. En Theme selecciona el valor @android:style/Theme.NoTitleBar.Fullscreen. Este tema visualizará la vista ocupando toda la pantalla, sin la barra de notificaciones ni el nombre de la aplicación.

6. Si en Theme pulsas el botón Browse… y seleccionas el botón circular System Resources puedes ver una lista de estilos definidos en el sistema.

NOTA. En algunas instalaciones esta lista puede que no te funcione.

7. Ejecuta la aplicación en un terminal real y verifica el resultado.

NOTA: en un emulador si cambias la orientación (Crtl-F11) esta cambiará igualmente. Se trata de un error de simulación, al no soportar esta configuración.

{jcomments on}