Bienvenidos sean a este post, hoy volveremos a nuestra app Asteroides para comenzar con lo verdaderamente interesante es decir la vista del juego en si, para ello deberemos descargar el siguiente archivo asteroides2 para poder agregar las imagenes a utilizar por los siguiente posts, estas las debemos agregar a nuestro recurso drawable, para ello deben extraer todos los archivos al disco, luego abrimos la app Asteroides con Android Studio, una vez abierto procederemos a agregar nuestras imagenes en el recurso drawable, primero copiaremos los archivos extraidos en el equipo por medio de Ctrl+C o con el boton derecho Copiar

Anuncios
android35

Luego iremos a nuestro editor y sobre el recurso drawable haremos Ctrl+V o click con el boton derecho y elegir Paste

android36

Una vez hecho esto nos aparecera el siguiente cuadro

android37

Aca elegiremos en que recurso copiaremos nuestros archivos, deben usar el seleccionado en la imagen, pulsen Ok para pasar a la siguiente ventana

android38

Aca nos preguntaran por ultima vez si este es el destino final de nuestros archivos, dejen todo como aparece y pulsen Ok para proceder con la copia. Una vez copiado, nos quedara de la siguiente forma

android39

Con todos nuestros graficos cargados en el recurso drawable ahora procederemos a crear una clase llamada Juego, para ello debemos hacer click con el boton derecho sobre el recurso contenedor de nuestra clases Java, elegiremos New -> Java class

android73
Anuncios

Una vez seleccionado nos aparecera el siguiente cuadro

android40

Aca modificaremos el campo Name por Juego, luego dejamos todo como se ve en  a imagen y pulsamos Ok para generar nuestra nueva clase. Ahora abramos nuestra nueva clase y veamos su codigo actual:

package org.example.asteroides;

public class Juego {
}

Lo reemplazaremos con este codigo:

package org.example.asteroides;

import android.app.Activity;
import android.os.Bundle;

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

Como pueden ver modificamos la clase para poder mostrar el contenido de un layout inexistente y a su vez extiende a la clase Activity, observen como se agregaron dos nuevos paquetes por medio de import, como dijimos carga un layout inexistente por ende generando un error para corregir esto procedamos con la creacion del nuevo layout para ello deben hacer click con el boton derecho sobre el recurso layout y seleccionar New -> Layout resource file como se ve en la siguiente imagen

android83

Una vez seleccionado aparecera el siguiente cuadro

android41

Aca modificaremos File Name por juego, luego Root element por LinearLayout y el resto debe quedar como se ve en la imagen, pulsen Ok para generar el archivo en cuestion, ahora abrimos el archivo, por ahora dejaremos este archivo como se genera y pronto lo modificaremos. Pasemos a generar una nueva clase, para ello como hicimos antes click con el boton derecho sobre el recurso contenedor de clases Java, eligen New -> Java class, cuando aparece el nuevo cuadro modifican el campo Name por Grafico, pulsan Ok para generar el archivo y modificamos su codigo actual por el siguiente:

Anuncios
package org.example.asteroides;

import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.view.View;

class Grafico {
    private Drawable drawable;
    private int cenX, cenY;
    private int ancho, alto;
    private double incX, incY;
    private double angulo, rotacion:
    private int radioColision;
    private int xAnterior, yAnterior;
    private int radioInval;
    private View view;

    public Grafico(View view, Drawable drawable){
        this.view = view;
        this.drawable = drawable;
        ancho = drawable.getIntrinsicWidth();
        alto = drawable.getIntrinsicHeight();
        radioColision = (alto + ancho) / 4;
        radioInval = (int) Math.hypot(ancho/2, alto/2);
    }

    public void dibujaGrafico(Canvas canvas){
        int x = cenX - ancho/2;
        int y = cenY - alto/2;
        drawable.setBounds(x, y, x+ancho, y+alto);
        canvas.save();
        canvas.rotate((float) angulo, cenX, cenY);
        drawable.draw(canvas);
        canvas.restore();
        view.invalidate(cenX - radioInval, cenY - radioInval,
                cenX + radioInval, cenY + radioInval);
        view.invalidate(xAnterior - radioInval, yAnterior - radioInval,
                xAnterior + radioInval, yAnterior + radioInval);
        xAnterior = cenX;
        yAnterior = cenY;
    }

    public void incrementaPos(double factor){
        cenX += incX * factor;
        cenY += incY * factor;
        angulo += rotacion * factor;
        if (cenX<0)
            cenX = view.getWidth();
        if (cenX>view.getWidth())
            cenX=0;
        if (cenY<0)
            cenY = view.getHeight();
        if (cenY>vew.getHeight())
            cenY = 0;
    }

    public double distancia(Grafico g){
        return Math.hypot(cenX-g.cenX, cenY-g.cenY);
    }

    public boolean verificaColision(Grafico g){
        return (distancia(g) < (radioColision - g.radioColision));
    }
}

Comencemos a hablar un poco sobre esta clase, como pueden ver es bastante compleja pero no tanto como parece, primero creamos un objeto de tipo Drawable llamado drawable para generar la imagen a dibujar, luego tenemos dos variables llamadas cenX y cenY las cuales seran para almacenar el valor del centro de X e Y de nuestro grafico, dos variables llamadas ancho y alto para almacenar las dimensiones de la imagen, las variables incX e incY para almacenar la velocidad de desplazamiento, angulo y rotacion se encargaran de almacenar el valor del angulo y la velocidad de rotacion, radioColision se utilizara para determinar la colision, xAnterior e yAnterior son dos variables para almacenar la posicion anterior de X e Y, radioInval es el valor de radio usado para invalidate() y lo mismo para el ultimo objeto llamado view. Despues tendremos un constructor para nuestra clase donde asignaremos el valor a view, drawable, el valor a ancho y alto, calcular a radioColision, y obtener el valor de la hipotenusa de nuestro grafico. Despues tenemos un metodo llamado dibujaGrafico(), este hara la magia de poder mostrar un grafico en nuestra pantalla, primero debemos crear dos variables llamadas x e y, donde haremos un simple calculo de la diferencia entre la el centro de la imagen y el alto o ancho divido dos. Una vez obtenido estos datos lo utilizaremos junto al metodo setBounds() para establecer el marco donde ira la imagen, en la siguiente linea la salvamos, nuestro siguiente metodo se llama rotate() y este se encargara  de crear la rotacion de la imagen por medio de matriz de transformacion, luego de efectuada la rotacion utilizamos un metodo para restaurar la matriz de transformacion para que no se aplique en futuros operaciones del canvas. En el siguiente caso tenemos dos metodos invalidate() la cual informara a la vista que debe ser redibujado, como pueden ver le indicamos los parametros del rectangulo creados por el setBounds() esto es para no tener que redibujar toda la vista sino solamente a nuestro grafico canvas, y como pueden ver son dos porque el primero es el encargado del rectangulo y el segundo se encargaria de la rotacion efectuada a nuestra imagen y por ultimo almacenaremos los valores de cenX y cenY en xAnterior y yAnterior respectivamente y ahora pasemos al siguiente metodo incrementaPos(), este metodo se encarga de modificar la posicion y el angulo segun la velocidad de translacion y el parametro factor es el encargado de ajustar la velocidad. Nuestras siguiente lineas se encargan de chequear si el objeto sale por uno de los limites de la pantalla y hacerlo aparecer por el lado contrario del mismo, tomemos la primera linea de ejemplo:

if (cenX<0)
    cenX = view.getWidth();

Como pueden ver si verifica que cenX es menor a cero le setea el valor proveniente del ancho de la vista por el metodo mostrado ahi, veamos el caso contrario:

if (cenX>view.getWidth())
    cenX=0;

Aca vemos como si se llega al ancho de la pantalla seteara el valor de cero para ir al lado contrario, esto mismo sirve para cenY. Por ultimo tenemos dos metodos encargados uno de calcular el valor de hipotenusa y retornarlo, y el otro es el encargado de verificar la colision entre los graficos. Como declaramos nuestras variables y objetos como private deberemos crear un Getters and Setters, esto es para tener acceso desde fuera de la clase, para ello deberemos hacer lo siguiente:

Anuncios

Primero debemos hacer click con el boton derecho sobre el codigo de nuestra clase Juegos

android42

Cuando aparezca este menu deben seleccionar Generate… y una vez seleccionado nos aparecera el siguiente cuadro

android43

En el nuevo cuadro deben seleccionar Getter And Setter donde luego aparecera el siguiente cuadro

android44

Aca deberan seleccionar todos los elementos que aparecen en el nuevo cuadro, pulsen Ok para generar todos los metodos get() y set() en nuestro codigo, nuestro codigo quedara de la siguiente manera:

package org.example.asteroides;

import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.view.View;

class Grafico {
    private Drawable drawable;
    private int cenX, cenY;
    private int ancho, alto;
    private double incX, incY;
    private double angulo, rotacion;
    private int radioColision;
    private int xAnterior, yAnterior;
    private int radioInval;
    private View view;
    
    public Grafico(View view, Drawable drawable){
        this.view = view;
        this.drawable = drawable;
        ancho = drawable.getIntrinsicWidth();
        alto = drawable.getIntrinsicHeight();
        radioColision = (alto + ancho) / 4;
        radioInval = (int) Math.hypot(ancho/2, alto/2);
    }

    public void dibujaGrafico(Canvas canvas){
        int x = cenX - ancho/2;
        int y = cenY - alto/2;
        drawable.setBounds(x, y, x+ancho, y+alto);
        canvas.save();
        canvas.rotate((float) angulo, cenX, cenY);
        drawable.draw(canvas);
        canvas.restore();
        view.invalidate(cenX - radioInval, cenY - radioInval,
                cenX + radioInval, cenY + radioInval);
        view.invalidate(xAnterior - radioInval, yAnterior - radioInval,
                xAnterior + radioInval, yAnterior + radioInval);
    }

    public void incrementaPos(double factor){
        cenX += incX * factor;
        cenY += incY * factor;
        angulo += rotacion * factor;
        if (cenX<0)
            cenX = view.getWidth();
        if (cenX>view.getWidth())
            cenX=0;
        if (cenY<0)
            cenY = view.getHeight();
        if (cenY>0)
            cenY = 0;
    }

    public double distancia(Grafico g){
        return Math.hypot(cenX-g.cenX, cenY-g.cenY);
    }

    public boolean verificaColision(Grafico g){
        return (distancia(g) < (radioColision - g.radioColision));
    }

    public Drawable getDrawable() {
        return drawable;
    }

    public void setDrawable(Drawable drawable) {
        this.drawable = drawable;
    }

    public int getCenX() {
        return cenX;
    }

    public void setCenX(int cenX) {
        this.cenX = cenX;
    }

    public int getCenY() {
        return cenY;
    }

    public void setCenY(int cenY) {
        this.cenY = cenY;
    }

    public int getAncho() {
        return ancho;
    }

    public void setAncho(int ancho) {
        this.ancho = ancho;
    }

    public int getAlto() {
        return alto;
    }

    public void setAlto(int alto) {
        this.alto = alto;
    }

    public double getIncX() {
        return incX;
    }

    public void setIncX(double incX) {
        this.incX = incX;
    }

    public double getIncY() {
        return incY;
    }

    public void setIncY(double incY) {
        this.incY = incY;
    }

    public double getAngulo() {
        return angulo;
    }

    public void setAngulo(double angulo) {
        this.angulo = angulo;
    }

    public double getRotacion() {
        return rotacion;
    }

    public void setRotacion(double rotacion) {
        this.rotacion = rotacion;
    }

    public int getRadioColision() {
        return radioColision;
    }

    public void setRadioColision(int radioColision) {
        this.radioColision = radioColision;
    }

    public int getxAnterior() {
        return xAnterior;
    }

    public void setxAnterior(int xAnterior) {
        this.xAnterior = xAnterior;
    }

    public int getyAnterior() {
        return yAnterior;
    }

    public void setyAnterior(int yAnterior) {
        this.yAnterior = yAnterior;
    }

    public int getRadioInval() {
        return radioInval;
    }

    public void setRadioInval(int radioInval) {
        this.radioInval = radioInval;
    }

    public View getView() {
        return view;
    }

    public void setView(View view) {
        this.view = view;
    }
    
}
Anuncios

Tengan en cuenta este metodo para generar los metodos get() y set() sin tener que tipear demasiado y ahorrando tiempo y posibles errores. Ahora pasaremos a nuestra clase llamada VistaJuego, la clase faltante del principio y la cual nos permitira ejecutar nuestra app, pongamos manos a la obra.
Como estuvimos haciendo hasta ahora, debemos hacer click con el boton derecho sobre el contenedor de las clases Java, seleccionan New -> Java class, aparecera un nuevo cuadro donde modificaran el campo Name por VistaJuego, el resto debe quedar sin modificar y pulsen Ok para generar el mismo, abrimos nuestro archivo y lo modificaremos de la siguiente manera:

package org.example.asteroides;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

import java.util.Vector;

public class VistaJuego extends View {
    private Vector< Grafico > asteroides;
    private int numAsteroides = 5;
    private int numFragmentos = 3;

    public VistaJuego(Context context, AttributeSet attrs){
        super(context, attrs);
        Drawable drawableNave, drawableAsteroide, drawableMisil;
        drawableAsteroide = context.getResources().getDrawable(
                R.drawable.asteroide1);
        asteroides = new Vector< Grafico >();
        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);
        for(Grafico asteroide: asteroides){
            asteroide.setCenX((int) (Math.random() * ancho));
            asteroide.setCenY((int) (Math.random() * alto));
        }
    }

    @Override
    protected void onDraw(Canvas canvas){
        super.onDraw(canvas);
        for(Grafico asteroide: asteroides){
            asteroide.dibujaGrafico(canvas);
        }
    }
}

En esta nueva clase haremos la base para nuestro juego, primero noten como hace una extension de View, luego vamos a crear un objeto de la clase Vector pero a su vez de la clase Grafico, luego dos variables para almacenar la cantidad de asteroides (numAsteroides) y la cantidad de fragmentos (numFragmentos), luego tenemos nuestro constructor donde crearemos tres objetos de la clase Drawable, uno para la nave (drawableNave), otro para el misil (drawableMisil) y el ultimo para el asteroide (drawableAsteroide) y el que por ahora vamos a trabajar, luego en asteroides lo iniciaremos con un Vector, para generar un Array, de la clase Grafico, despues utlizaremos un bucle for para crear de forma a la azar nuestros asteroides, y su cantidad va a depender de numAsteroides, luego tendremos la modificacion de los metodos onSizeChanged() y onDraw(), el primero para cuando cambie de tamaño nuestro drawable y el siguiente metodo se encargara de mostrar el grafico, en ambos utilizamos un bucle for avanzado, el cual a diferencia del for convencional necesita un Array para crear el ciclo y este sera en base a la cantidad de elementos en el mismo. Ahora vamos a hacer las ultimas modificaciones para probar nuestra app. Primero debemos abrir el archivo AndroidManifest.xml para registrar nuestra actividad, a este archivo le agregaremos la siguiente linea:

< activity android:name=".Juego"
    android:label="@string/tituloJuego"/ >

Esta debe ser agregada junto a las otras actividades, procedamos a crear un nuevo metodo pero en esta ocasion dentro de la clase MainActivity, agregaremos el siguiente metodo:

public void lanzarJuego(View view){
    Intent i = new Intent(this, Juego.class);
    startActivity(i);
}

Una vez agregada nuestro nuevo metodo solo nos resta crear el evento listener, para ello debemos ir al archivo activity_main.xml y modificar el siguiente elemento:

Anuncios
<Button
    android:id="@+id/botJugar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/botonJugar"
    android:onClick="mostrarPreferencias"
    />

Le debemos hacer la siguiente modificacion:

<Button
    android:id="@+id/botJugar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/botonJugar"
    android:onClick="lanzarJuego"
    />

Recuerden modificar esta elemento en la vista apaisada (land) de la activity_main.xml y nuestro ultimo paso sera modificar juego.xml, como dijimos al principio, para poder ver nuestro juego, veamos el nuevo codigo de nuestro archivo:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.tinchicus.asteroides.VistaJuego
        android:id="@+id/VistaJuego"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:focusable="true"
        android:background="@drawable/fondo" />
</LinearLayout>

Como pueden ver agregamos la vista VistaJuego para poder finalmente poder probar nuestra app, una vez salvado podremos probarlo como se ve en el siguiente video

Y asi se ve de forma apaisada (land)

Como pueden ver en los videos cuando tocamos el boton Jugar se carga nuestra vista con el fondo y genera cinco asteroides con ubicaciones a la azar pero por ahora sin movimientos ni interaccion.

Anuncios

En resumen, hoy hemos puesto en practica parte de lo visto en nuestros posts anteriores sobre tipo de graficos, tambien hemos creado nuestra vista principal del juego y hemos agregado nuestros elementos en la misma, hemos visto como calcular las posiciones de forma aleatoria y hemos repasado como se crea los get() y set() por medio del Android Studio, espero les haya sido util, sigueme en Twitter, Facebook o Google+ para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00