Bienvenidos sean a este post, despues de que hemos visto un poco sobre como son los eventos de los usuarios y como trabaja el evento touch procedamos a agregar la posibilidad de manejar nuestra nave en Asteroides, para ello vamos a poner manos a la obra.
Abramos Android Studio, la app Asteroides y vamos a la clase VistaJuego donde agregaremos el siguiente codigo:

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

En este nuevo metodo modificaremos a onTouchEvent() para lo cual previamenete deberemos crear tres variables dos de tipo float llamadas mX y mY con valor cero y uno de tipo boolean llamado disparo al cual le setearemos como false.Volviendo a nuestro nuevo metodo, primero vamos a crearla por medio de super, luego crearemos dos variables de tipo float llamadas x, a la cual por medio de MotionEvent llamado event le asignamos un valor por medio del metodo getX(), y otra llamada y para hacer lo mismo que en la otra variable pero con el metodo getY(), nuestro siguiente paso sera establecer un switch(), para determinar por medio de cases las coincidencias con algunas constantes, veamos cada uno de los cases:

  • case MotionEvent.ACTION_DOWN, en este caso seteara a disparo como verdadero (true)
  • case MotionEvent.ACTION_MOVE, en este caso primero creara dos variables para almacenar la diferencia entre los x e y con mX y mY respectivamente, para luego por medio de dos condicionales establecen el valor de giroNave o aceleracionNave.
  • case MotionEvent.ACTION_UP, como se solto la pantalla establece a giroNave y aceleracionNave en cero y por ultimo si disparo es verdadero (true) o tambien puede decirse que existe, llamara al metodo activaMisil() pero como ven esta comentado porque por ahora este metodo no existe.

Nuestro codigo final de VistaJuego queda de la siguiente manera:

package com.tinchicus.asteroides;

import android.content.Context;
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.Shape;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import java.util.Vector;

public class VistaJuego extends View {
    // //// ASTEROIDES //////
    private Vector<Grafico> asteroides;
    private int numAsteroides = 5;
    private int numFragmentos = 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;

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

        SharedPreferences pref = PreferenceManager
                .getDefaultSharedPreferences(getContext());
        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);
            ShapeDrawable dAsteroide = new ShapeDrawable(
                    new PathShape(pathAsteroide,1,1));
            dAsteroide.getPaint().setColor(Color.WHITE);
            dAsteroide.getPaint().setStyle(Paint.Style.STROKE);
            dAsteroide.setIntrinsicWidth(50);
            dAsteroide.setIntrinsicHeight(50);
            drawableAsteroide = dAsteroide;
            setBackgroundColor(Color.BLACK);
            //setLayerType(View.LAYER_TYPE_SOFTWARE,null);
        } else {
            drawableAsteroide = context.getResources().getDrawable(
                    R.drawable.asteroide1);
            //setLayerType(View.LAYER_TYPE_HARDWARE,null);
        }
        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);
        }
        asteroides = new Vector< Grafico >();
        nave = new Grafico(this,drawableNave);
        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
    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:
                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
    synchronized protected void onDraw(Canvas canvas){
        super.onDraw(canvas);
        for(Grafico asteroide: asteroides){
            asteroide.dibujaGrafico(canvas);
        }
        nave.dibujaGrafico(canvas);
    }

    synchronized 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);
        }
    }

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

Si probamos nuestra app podremos ver como funciona, en este caso les muestro un video en mi emulador

Como pueden ver en el emulador no se muestra perfectamente el funcionamiento, por este motivo les recomiendo probar directamente en un celular o taablet, lo que tengan disponible, porque tiene una mejor performance y se ve mejor el funcionamiento, ahora si no saben como transferirlo al dispositivo les recomiendo este post donde explico el paso a paso como hacerlo.
En resumen, hoy le hemos agregado interactividad verdadera a nuestro juego, permitiendo algunos movimientos basicos de nuestra nave, hemos visto como con un simple en nuestra vista se nos abrio un mundo de posibilidades, 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.

Anuncios