Bienvenidos sean a este post, hoy veremos como agregar un cartel de victoria y de derrota a nuestro juego Asteroides, para ello debemos abrir Android Studio y nuestro proyecto Asteroides, si no tienes este juego puedes hacerlo siguiendo estos posts:

Anuncios

Una vez creado el juego vamos a la clase VistaJuego y agregaremos las siguientes variables:

    // //// ESTADOS DE JUEGO //////
    public static final int ESTADO_JUGANDO = 0;
    public static final int ESTADO_VICTORIA = 1;
    public static final int ESTADO_DERROTA = 2;
    
    private int estado = ESTADO_JUGANDO;
    private View vistaVictoria;
    private View vistaDerrota;

Las tres primeras son constantes y seran para identificar cada estado del juego (Jugando, ganar, perder), despues crearemos una variable llamada estado donde le asignaremos el valor de ESTADO_JUGANDO, para luego crear dos objetos de tipo View que despues usaremos para vincular el codigo Java con el Layout, en la misma clase añadiremos los siguientes dos metodos:

    public void setVistaVictoria(View vista){
        vistaVictoria=vista;
    }
    
    public void setVistaDerrota(View vista){
        vistaDerrota=vista;
    }

Estos dos metodos se encargaran de asignar a los objetos vistaVictoria o vistaDerrota la vista informada como argumento, por ahora solamente hace esto, nuestra siguiente modificacion sera en onDraw() donde agregaremos el siguiente bloque:

        if (estado == ESTADO_VICTORIA){
            vistaVictoria.setVisibility(VISIBLE);
        } else if (estado == ESTADO_DERROTA){
            vistaDerrota.setVisibility(VISIBLE);
        }

En este caso verificara el valor de estado, si es igual a ESTADO_VICTORIA, procedera a hacer vistaVictoria visible por medio de setVisibility(), de lo contrario si es igual a ESTADO_DERROTA procedera a hacer visible a vistaDerrota de la misma forma que la anterior, nuestra siguiente modificacion sera en destruyeAsteroide() donde modificaremos este condicional:

        if(asteroides.isEmpty()){
            salir();
        }

De esta forma:

        if(asteroides.isEmpty()){
            estado = ESTADO_VICTORIA;
            salir();
        }

Este condicional chequea que no existan mas asteroides o trozos de los mismos y procede a setear a estado con ESTADO_VICTORIA, esto ocasionara que nos muestre el proximo cartel de victoria, ya haremos uno, y luego la posibilidad de agregar nuestro nombre en la lista de putuaciones, nuestra siguiente modificacion sera en actualizarFisica() donde tenemos este bucle for:

        for(Grafico asteroide : asteroides){
            if(asteroide.verificaColision(nave))
                salir();
        }

Y lo modificaremos de la siguiente forma:

        for(Grafico asteroide : asteroides){
            if(asteroide.verificaColision(nave))
                estado = ESTADO_DERROTA;
                salir();
        }

En este caso cambiaremos el valor de estado a ESTADO_DERROTA para notificar que perdimos y mostrar el cartel pertinente, ya lo haremos, y por medio de salir() veremos la posibilidad de agregar nuestro nombre a la lista de puntuaciones, nuestra siguiente modificacion sera en el metodo salir() donde podemos borrar (o comentar) la linea: padre.finish(), tal como se ve a continuacion:

    private void salir(){
        Bundle bundle = new Bundle();
        bundle.putInt("puntuacion", puntuacion);
        Intent intento = new Intent();
        intento.putExtras(bundle);
        padre.setResult(Activity.RESULT_OK,intento);
        // padre.finish();
    }
Anuncios

Esto solamente sera para mostrar la notificacion de ingreso de puntuaciones pero no matara a la clase Juego, nuestra siguiente modificacion sera para el layout juego.xml donde cambiaremos el codigo actual por el siguiente:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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" />
    <TextView
        android:id="@+id/Victoria"
        android:text="Has Ganado!!"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textSize="35sp"
        android:textColor="#FF0000"
        android:background="#7F000000"
        android:visibility="invisible"/>
    <TextView
        android:id="@+id/Derrota"
        android:text="Game Over, Man!!"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textSize="35sp"
        android:textColor="#FF0000"
        android:background="#7F000000"
        android:visibility="invisible"/>
</FrameLayout>

En este caso no solamente cambiamos de LinearLayout por FrameLayout sino que a su vez agregamos dos TextView para mostrar la victoria y la derrota llamados Victoria y Derrota respectivamente, seguimos manteniendo el fondo pero la unica diferencia es que ambos TextView ocuparan toda la pantalla y no se veran porque en visibility los hicimos invisible, si recuerdan en onDraw nosotros le dijimos con el condicional que agregamos que dependiendo de estado se vuelven visible vistaVictoria o vistaDerrota, nuestra siguiente modificacion sera en la clase Juego donde primero agregaremos el siguiente objeto:

VistaJuego vistaJuego;

Y luego modificaremos el metodo onCreate() de la clase Juego para agregar las siguientes lineas:

        vistaJuego = (VistaJuego) findViewById(R.id.VistaJuego);
        vistaJuego.setVistaVictoria(findViewById(R.id.Victoria));
        vistaJuego.setVistaDerrota(findViewById(R.id.Derrota));

En este caso asociaremos a vistaJuego con el layout VistaJuego y luego usaremos los metodos setVistaVictoria() y setVistaDerrota() que agregamos anteriormente para asociarlos a sus respectivos elementos del layout, veamos a continuacion como quedo el codigo de la clase VistaJuego:

package org.example.asteroides;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.graphics.drawable.shapes.RectShape;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.preference.PreferenceManager;

import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;

import java.util.List;
import java.util.Vector;

public class VistaJuego extends View implements SensorEventListener {
    // //// CONFIG //////
    SharedPreferences pref = PreferenceManager
            .getDefaultSharedPreferences(getContext());
    private int puntuacion = 0;
    private static Activity padre;

    // //// ASTEROIDES //////
    private Drawable drawableAsteroide[] = new Drawable[3];
    private Vector<Grafico> asteroides;
    private int numAsteroides = 5;
    private int numFragmentos = Integer.parseInt(pref
            .getString("fragmentos","3"));

    // //// NAVE //////
    private Grafico nave;
    private int giroNave;
    private double aceleracionNave;
    private static final int MAX_VELOCIDAD_NAVE = 20;
    private static final int PASO_GIRO_NAVE = 5;
    private static final float PASO_ACELERACION_NAVE = 0.5f;

    // //// THREAD y TIEMPO //////
    private ThreadJuego hilo = new ThreadJuego();
    private static int PERIODO_PROCESO = 50;
    private long ultimoProceso = 0;

    // //// MISIL //////
    private Grafico misil;
    private static int PASO_VELOCIDAD_MISIL = 12;
    private boolean misilActivo = false;
    private int tiempoMisil;

    // //// MULTIMEDIA //////
    SoundPool soundPool;
    int idDisparo, idExplosion;

    // //// ESTADOS DE JUEGO //////
    public static final int ESTADO_JUGANDO = 0;
    public static final int ESTADO_VICTORIA = 1;
    public static final int ESTADO_DERROTA = 2;

    private int estado = ESTADO_JUGANDO;
    private View vistaVictoria;
    private View vistaDerrota;

    public VistaJuego(Context context, AttributeSet attrs){
        super(context, attrs);
        Drawable drawableNave, drawableMisil;

        if (pref.getString("graficos","1").equals("0")){
            Path pathAsteroide = new Path();
            pathAsteroide.moveTo((float) 0.3, (float) 0.0);
            pathAsteroide.lineTo((float) 0.6, (float) 0.0);
            pathAsteroide.lineTo((float) 0.6, (float) 0.3);
            pathAsteroide.lineTo((float) 0.8, (float) 0.2);
            pathAsteroide.lineTo((float) 1.0, (float) 0.4);
            pathAsteroide.lineTo((float) 0.8, (float) 0.6);
            pathAsteroide.lineTo((float) 0.9, (float) 0.9);
            pathAsteroide.lineTo((float) 0.8, (float) 1.0);
            pathAsteroide.lineTo((float) 0.4, (float) 1.0);
            pathAsteroide.lineTo((float) 0.0, (float) 0.6);
            pathAsteroide.lineTo((float) 0.0, (float) 0.2);
            pathAsteroide.lineTo((float) 0.3, (float) 0.0);
            for(int i=0; i < 3; i++){
                ShapeDrawable dAsteroide = new ShapeDrawable(
                        new PathShape(pathAsteroide,1,1));
                dAsteroide.getPaint().setColor(Color.WHITE);
                dAsteroide.getPaint().setStyle(Paint.Style.STROKE);
                dAsteroide.setIntrinsicHeight(50 - i * 14);
                dAsteroide.setIntrinsicWidth(50 - i * 14);
                drawableAsteroide[i] = dAsteroide;
            }
            setBackgroundColor(Color.BLACK);
        } else {
            drawableAsteroide[0] = context.getResources()
                    .getDrawable(R.drawable.asteroide1);
            drawableAsteroide[1] = context.getResources()
                    .getDrawable(R.drawable.asteroide2);
            drawableAsteroide[2] = context.getResources()
                    .getDrawable(R.drawable.asteroide3);
        }
        if (pref.getString("graficos","1").equals("0")){
            Path pathNave = new Path();
            pathNave.moveTo((float)0.0,(float)0.0);
            pathNave.lineTo((float)1.0,(float)0.5);
            pathNave.lineTo((float)0.0,(float)1.0);
            pathNave.lineTo((float)0.0,(float)0.0);
            ShapeDrawable dNave = new ShapeDrawable(
                    new PathShape(pathNave,1,1));
            dNave.getPaint().setColor(Color.WHITE);
            dNave.getPaint().setStyle(Paint.Style.STROKE);
            dNave.setIntrinsicHeight(15);
            dNave.setIntrinsicWidth(20);
            drawableNave = dNave;

        } else {
            drawableNave = context.getResources().getDrawable(
                    R.drawable.nave);
        }
        if (pref.getString("graficos","1").equals("0")){
            ShapeDrawable dMisil = new ShapeDrawable(new RectShape());
            dMisil.getPaint().setColor(Color.WHITE);
            dMisil.getPaint().setStyle(Paint.Style.STROKE);
            dMisil.setIntrinsicWidth(15);
            dMisil.setIntrinsicHeight(3);
            drawableMisil = dMisil;
        } else {
            drawableMisil = context.getResources().getDrawable(
                    R.drawable.misil1);
        }

        asteroides = new Vector<Grafico>();
        nave = new Grafico(this,drawableNave);
        misil = new Grafico(this,drawableMisil);

        for(int i = 0; i < numAsteroides; i++) {
            Grafico asteroide = new Grafico(this, drawableAsteroide[0]);
            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);
        }

        SensorManager mSensorManager = (SensorManager)
                context.getSystemService(Context.SENSOR_SERVICE);
        List<Sensor> listaSensores = mSensorManager.getSensorList(
                Sensor.TYPE_ORIENTATION);
        if (!listaSensores.isEmpty()){
            Sensor orientacionSensor = listaSensores.get(0);
            mSensorManager.registerListener(this,orientacionSensor,
                    SensorManager.SENSOR_DELAY_GAME);
        }

        soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC,0);
        idDisparo = soundPool.load(context,R.raw.disparo,0);
        idExplosion = soundPool.load(context,R.raw.explosion,0);

    }

    @Override
    public boolean onKeyDown(int codigoTecla, KeyEvent evento){
        super.onKeyDown(codigoTecla, evento);
        boolean procesada = true;
        switch(codigoTecla) {
            case KeyEvent.KEYCODE_DPAD_UP:
                aceleracionNave = +PASO_ACELERACION_NAVE;
                break;
            case KeyEvent.KEYCODE_DPAD_LEFT:
                //Toast.makeText(this, "Izquierda", Toast.LENGTH_SHORT).show();
                giroNave = -PASO_GIRO_NAVE;
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                giroNave = +PASO_GIRO_NAVE;
                break;
            case KeyEvent.KEYCODE_ENTER:
                activaMisil();
                break;
            default:
                procesada = false;
                break;
        }
        return procesada;
    }

    @Override
    public boolean onKeyUp(int codigoTecla, KeyEvent evento){
        super.onKeyUp(codigoTecla, evento);
        boolean procesada = true;
        switch(codigoTecla) {
            case KeyEvent.KEYCODE_DPAD_UP:
                aceleracionNave = 0;
                break;
            case KeyEvent.KEYCODE_DPAD_LEFT:
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                giroNave = 0;
                break;
            default:
                procesada = false;
                break;
        }
        return procesada;
    }

    @Override
    protected void onSizeChanged(int ancho, int alto, int ancho_anter,
                                 int alto_anter){
        super.onSizeChanged(ancho,alto,ancho_anter,alto_anter);
        nave.setCenY(alto/2);
        nave.setCenX(ancho/2);
        for(Grafico asteroide: asteroides){
            do {
                asteroide.setCenX((int) (Math.random() * ancho));
                asteroide.setCenY((int) (Math.random() * alto));
            } while(asteroide.distancia(nave) < (ancho+alto)/5);
        }
        ultimoProceso = System.currentTimeMillis();
        hilo.start();
    }

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

        if (estado == ESTADO_VICTORIA){
            vistaVictoria.setVisibility(VISIBLE);
        } else if (estado == ESTADO_DERROTA){
            vistaDerrota.setVisibility(VISIBLE);
        }
    }

    protected void actualizarFisica(){
        long ahora = System.currentTimeMillis();
        if (ultimoProceso + PERIODO_PROCESO > ahora){
            return;
        }
        double retardo = (ahora - ultimoProceso) / PERIODO_PROCESO;
        ultimoProceso = ahora;
        nave.setAngulo((int) (nave.getAngulo() + giroNave * retardo));
        double nIncX = nave.getIncX() + aceleracionNave *
                Math.cos(Math.toRadians(nave.getAngulo())) * retardo;
        double nIncY = nave.getIncY() + aceleracionNave *
                Math.sin(Math.toRadians(nave.getAngulo())) * retardo;
        if (Math.hypot(nIncX,nIncY) <= MAX_VELOCIDAD_NAVE){
            nave.setIncX(nIncX);
            nave.setIncY(nIncY);
        }
        nave.incrementaPos(retardo);
        for(Grafico asteroide: asteroides){
            asteroide.incrementaPos(retardo);
        }
        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;
                    }
            }
        }
        for(Grafico asteroide : asteroides){
            if(asteroide.verificaColision(nave))
                estado = ESTADO_DERROTA;
                salir();
        }
    }

    class ThreadJuego extends Thread {
        @Override
        public void run(){
            while(true){
                actualizarFisica();
            }
        }
    }

    private float mX=0, mY=0;
    private boolean disparo=false;

    @Override
    public boolean onTouchEvent(MotionEvent event){
        super.onTouchEvent(event);
        float x = event.getX();
        float y = event.getY();
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                disparo=true;
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = Math.abs(x - mX);
                float dy = Math.abs(y - mY);
                if (dy<6 && dx>6) {
                    giroNave = Math.round((x - mX) / 2);
                    disparo = false;
                } else if(dx<6 && dy>6){
                    aceleracionNave = Math.round((mY - y)/2);
                    disparo = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                giroNave = 0;
                aceleracionNave = 0;
                if (disparo) {
                    activaMisil();
                }
                break;
        }
        mX = x; mY = y;
        return true;
    }


    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy){}

    private boolean hayValorinicial = false;
    private float valorInicial;

    @Override
    public void onSensorChanged(SensorEvent evento){
        float valor = evento.values[2];
        if (!hayValorinicial){
            valorInicial=valor;
            hayValorinicial=true;
        }
        giroNave = (int) (valor - valorInicial)/3;
    }

    private void destruyeAsteroide(int i){
        int tam;
        if (asteroides.get(i).getDrawable()!=drawableAsteroide[2]){
            if(asteroides.get(i).getDrawable()==drawableAsteroide[1]){
                tam=2;
            } else {
                tam=1;
            }
            for(int n = 0; n < numFragmentos; n++){
                Grafico asteroide = new Grafico(this,drawableAsteroide[tam]);
                asteroide.setCenY(asteroides.get(i).getCenY());
                asteroide.setCenX(asteroides.get(i).getCenX());
                asteroide.setIncX(Math.random() * 7-2-tam);
                asteroide.setIncY(Math.random() * 7-2-tam);
                asteroide.setAngulo((int) (Math.random() * 360));
                asteroide.setRotacion((int) (Math.random() * 8-4));
                asteroides.add(asteroide);
            }
        }
        synchronized (asteroides){
            asteroides.removeElementAt(i);
            misilActivo = false;
            soundPool.play(idExplosion,1,1,0,0,1);
        }

        puntuacion += 1000;

        if(asteroides.isEmpty()){
            estado = ESTADO_VICTORIA;
            salir();
        }
    }

    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;
        soundPool.play(idDisparo,1,1,1,0,1);
    }

    public static void setPadre(Activity padre){
        VistaJuego.padre = padre;
    }

    private void salir(){
        Bundle bundle = new Bundle();
        bundle.putInt("puntuacion", puntuacion);
        Intent intento = new Intent();
        intento.putExtras(bundle);
        padre.setResult(Activity.RESULT_OK,intento);
        // padre.finish();
    }

    public void setVistaVictoria(View vista){
        vistaVictoria=vista;
    }

    public void setVistaDerrota(View vista){
        vistaDerrota=vista;
    }

}

Y a continuacion veremos como quedo finalmente el codigo de nuestra clase Juego:

Anuncios
package org.example.asteroides;

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

public class Juego extends Activity {

    VistaJuego vistaJuego;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.juego);
        VistaJuego.setPadre(this);
        vistaJuego = (VistaJuego) findViewById(R.id.VistaJuego);
        vistaJuego.setVistaVictoria(findViewById(R.id.Victoria));
        vistaJuego.setVistaDerrota(findViewById(R.id.Derrota));
    }
}

Con todas estas modificaciones realizadas podemos proceder a probar nuestro juego como se ve en el siguiente video:

No esperen que les muestre el cartel de Victoria, porque en este emulador es imposible jugarlo, cuando perdemos podemos ver el mensaje de derrota y cuando al apretar el boton para volver y salir nos da la posibilidad de agregar nuestro nombre para la lista de puntuacione, si lograron lo visto en pantalla Felicidades!!! Ya tienen un juego casi apto para consumo humano 😄

Anuncios

En resumen, hoy hemos visto como poder agregar un cartel que notifique la victoria o la derrota en nuestro juego, como implementarlo y las modificaciones necesarias, espero les haya sido util sigueme en Twitter o Facebook 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