Bienvenidos sean a este post, hoy veremos como hacer una serie de modificaciones en nuestra app Asteroides, en caso de no tenerlo les recomiendo el post anterior donde estan todos los posts para crearlo hasta nuestra ultima modificacion, hoy nos centraremos en agregar la posibilidad de poder configurar la cantidad de fragmentos que se crearan cuando un asteroide sea impactado y la posibilidad de reproducir musica o no, primero hagamos la modificacion para permitirnos modificar la cantidad de fragmentos.

Anuncios

Para ello debemos ir a la clase VistaJuego y debemos sacar del constructor el siguiente bloque:

SharedPreferences pref = PreferenceManager
            .getDefaultSharedPreferences(getContext());

Este debemos ubicarlo al comienzo de la clase, por debajo del encabezado del mismo para transformarlo en forma global y nos permita no solamente ser utilizado por nuestro constructor sino tambien para implementarlo en nuestro programa, para ello deberemos modificar la siguiente linea:

private int numFragmentos = 3;

Por esta otra linea:

private int numFragmentos = Integer.parseInt(pref
.getString("fragmentos","3"));

Esta linea se encargara de buscar en el almacenamiento por defecto de nuestro juego pero esta informacion es guardada en formato en String y nosotros la necesitamos en formato int (entero) para ello usamos la clase Integer con el metodo parseInt() que el resultado obtenido sera transformado a int, vean como utilizamos al objeto pref para poder acceder a las preferencias y despues por medio de getString() le decimos que debe buscar la clave fragmentos para recuperar su valor y en caso de no encontrarlo utilizara el siguiente valor que actuaria como un valor por defecto, con esto ya tendriamos solucionado el tema de los fragmentos, nuestro siguiente paso sera modificar para poder configurar la reproduccion o no de la musica, para ello debemos ir a MainActivity y debemos modificar lo siguiente, para empezar agregaremos las siguientes lineas en el comienzo de nuestra clase:

private SharedPreferences pref;
private String estadomusica;

La primera se encargara de crear un objeto llamado pref de la clase SharedPreferences, la cual nos permitira acceder a las preferencias, la segunda linea se encargara de crear una cadena para almacenar el estado que obtendremos desde las preferencias, nuestra siguiente modificacion sera en el metodo onCreate() donde agregaremos las siguientes dos lineas:

pref = PreferenceManager.getDefaultSharedPreferences(this);
estadomusica = Boolean.toString(pref.getBoolean("musica",true));

La primera linea se encargara de asignarle al objeto pref la posibilidad de poder extraer informacion de las preferencias predeterminadas, les recomiendo este post para mas informacion, la segunda linea extraera el estado del elemento de chequeo utilizada para habilitar o no la musica, como la informacion es de tipo boolean (true o false) y nosotros necesitamos transformarla en String (cadena) utilizaremos el metodo toString() de la clase Boolean para transformar en este tipo la informacion obtenida por medio de getBoolean(), la primera palabra es la clave que debe buscar y extraer la informacion, el segundo valor es en caso de no encontrar el anterior, nuestra siguiente modificacion sera transformar las siguientes dos lineas:

mp = MediaPlayer.create(this, R.raw.audio);
mp.start();

En este bloque:

mp = MediaPlayer.create(this, R.raw.audio);
if (estadomusica.equals("true")) {
mp.start();
} else {
mp.start();
mp.pause();
}
Anuncios

Donde la primer linea se encargara de crear a nuestro mediaplayer y le asignara el archivo audio.mp3 del recurso raw, luego verificara si estadomusica tiene el valor true, en caso de ser verdadero ejecutara la musica de fondo en caso contrario ejecutara la musica pero inmediatamente la pausara, esto es asi porque al momento de probarlo si nosotros teniamos la opcion de no reproducir musica, es decir destildado por lo tanto en false, al volverlo a tildar y regresar a la pantalla inicial no reproducia la musica, a pesar de tener el objeto creado y asignado, pero de esta forma funciona correctamente lo cual veremos mas adelante, nuestra siguiente modificacion sera en onResume() porque es el otro encargado de reproducir cuando volvemos a MainActivity, para ello modificaremos el bloque:

    @Override
    protected void onResume(){
        super.onResume();
        Toast.makeText(this,"onResume",Toast.LENGTH_SHORT).show();
        mp.start();
    }

Por este otro:

    protected void onResume(){
        super.onResume();
        pref = PreferenceManager.getDefaultSharedPreferences(this);
        estadomusica = Boolean.toString(pref.getBoolean("musica",true));
        Toast.makeText(this,"onResume",Toast.LENGTH_SHORT).show();
        if (estadomusica.equals("true")) {
            mp.start();
        }
    }

Donde nuestras modificaciones mas importantes son el agregado de nuestras dos lineas para obtener el valor de musica y luego un condicional que solamente si estadomusica es verdadero (true) ejecuta la musica nuevamente, esto es para cuando volvemos con el estado onResume(), con todas estas modificaciones realizadas pasemos a ver como quedo primero el codigo de VistaJuego:

Anuncios
package org.example.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.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.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());

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

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

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

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

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

Y veamos como quedo finalmente el codigo de MainActivity:

package org.example.asteroides;

import android.content.Intent;
import android.content.SharedPreferences;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.Prediction;
import android.graphics.drawable.Animatable;
import android.media.MediaPlayer;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity
        implements GestureOverlayView.OnGesturePerformedListener{

    private GestureLibrary librearia;
    private MediaPlayer mp;
    private SharedPreferences pref;
    private String estadomusica;

    public static AlmacenPuntuaciones almacen =
            new AlmacenPuntuacionesArray();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        pref = PreferenceManager.getDefaultSharedPreferences(this);
        estadomusica = Boolean.toString(pref.getBoolean("musica",true));

        LinearLayout jugar = (LinearLayout) findViewById(R.id.principio);
        Animation animacion = AnimationUtils.loadAnimation(this,
                R.anim.aparecer);
        jugar.startAnimation(animacion);
        librearia = GestureLibraries.fromRawResource(this,R.raw.gestures);
        if (!librearia.load())
            finish();

        GestureOverlayView gestosView = (GestureOverlayView)
                findViewById(R.id.gestos);
        gestosView.addOnGesturePerformedListener(this);
        Toast.makeText(this,"onCreate",Toast.LENGTH_SHORT).show();
        mp = MediaPlayer.create(this, R.raw.audio);
        if (estadomusica.equals("true")) {
            mp.start();
        } else {
            mp.start();
            mp.pause();
        }

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

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

    public void mostrarPreferencias(View view){
        SharedPreferences pref =
                PreferenceManager.getDefaultSharedPreferences(this);
        String s = "música: " + pref.getBoolean("musica",true)
                + ", graficos: " + pref.getString("graficos","?")
                + ", fragmentos" + pref.getString("fragmentos","3");
        Toast.makeText(this,s,Toast.LENGTH_SHORT).show();
    }

    public void onGesturePerformed(GestureOverlayView ov, Gesture gesto){
        ArrayList<Prediction> predicciones = librearia.recognize(gesto);
        if (predicciones.size()>0){
            String comando = predicciones.get(0).name;
            if (comando.equals("play")){
                lanzarJuego(null);
            } else if(comando.equals("configurar")){
                lanzarPreferencias(null);
            } else if(comando.equals("acerca_de")){
                lanzarAcercaDe(null);
            } else if(comando.equals("cancelar")){
                finish();
            }
        }
    }

    @Override
    protected void onStart(){
        super.onStart();
        Toast.makeText(this,"onStart",Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onResume(){
        super.onResume();
        pref = PreferenceManager.getDefaultSharedPreferences(this);
        estadomusica = Boolean.toString(pref.getBoolean("musica",true));
        Toast.makeText(this,"onResume",Toast.LENGTH_SHORT).show();
        if (estadomusica.equals("true")) {
            mp.start();
        }
    }

    @Override
    protected void onPause(){
        super.onPause();
        Toast.makeText(this,"onPause",Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onStop(){
        super.onStop();
        Toast.makeText(this,"onStop",Toast.LENGTH_SHORT).show();
        mp.pause();
    }

    @Override
    protected void onRestart(){
        super.onRestart();
        Toast.makeText(this,"onRestart",Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        Toast.makeText(this,"onDestroy",Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onSaveInstanceState(Bundle estadoGuardado){
        super.onSaveInstanceState(estadoGuardado);
        if (mp!=null){
            int pos = mp.getCurrentPosition();
            estadoGuardado.putInt("posicion",pos);
        }
    }

    @Override
    protected void onRestoreInstanceState(Bundle estadoGuardado){
        super.onRestoreInstanceState(estadoGuardado);
        if (estadoGuardado!=null && mp!=null){
            int pos = estadoGuardado.getInt("posicion");
            mp.seekTo(pos);
        }
    }
}

Ahora si con todas nuestras modificaciones realizadas procedamos a probar nuestra app como se ve en el siguiente video

En este caso vemos como podemos habilitar o deshabilitar la musica, tambien vemos como pudimos variar la cantidad de fragmentos que se dividira los asteroides cada vez que son impactados, con esto ya logramos modificar los estados de nuestra app con las configuraciones de la misma y como hacerlo de manera simple con las preferencias por defecto de Android.

Anuncios

En resumen, hoy hemos como recuperar informacion de las preferencias, como modificarla en una clase y que estas preferencias afectan a la conducta del programa, 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