Anuncios

Bienvenidos sean a este post, antes de comenzar con nuestro tercer proyecto habiamos hablado sobre arrays y hoy lo pondremos en practica para representar nuestras balas.

Anuncios

Para comenzar volveremos a nuestra clase BalaceraJuego, en la parte donde tenemos nuestras variables declaradas agregaremos el siguiente bloque:

    private Bala [] mBalas = new Bala[10000];
    private int mNumBalas = 0;
    private int mGeneracionRatio = 1;
    private Random mRandomX = new Random();
    private Random mRandomY = new Random();
Anuncios
Anuncios

La primer linea sera la encargada de crear un array para nuestras balas, para ello usamos la clase Bala y asignamos un tamaño de 10000 posiciones lo cual significa que podemos generar 10000 balas, la siguiente variable sera para indicar la cantidad de balas que tenemos, la tercer linea se encarga de indicar cual va a ser nuestro margen de generacion de balas, las siguientes dos lineas simplemente generaran un numero al azar por medio de Random primero para el eje X y otro para el eje Y, con esto podemos generar nuestra bala de una posicion completamente al azar, al igual que en el post anterior agregaremos el siguiente bloque en el constructor delante del llamado a la funcion iniciaJuego:

        for(int a=0; a < mBalas.length; a++){
            mBalas[a]=new Bala(mScreenX);
        }
Anuncios

En este ciclo contaremos 10000 numeros, esto gracias a length que es un propiedad que nos devuelve la cantidad de posiciones disponibles en el array, y por cada ciclo generaremos una nueva bala, mejor dicho un nuevo objeto de la clase Bala, con esto tenemos nuestro objetos de balas creadas pero esto no significa que ya las tenemos en pantalla sino simplemente los contenedores, pasemos a la siguiente modificacion en este metodo:

    private void actualizar(){
        for(int i = 0; i < mNumBalas; i++){
            mBalas[i].actualizar(mFPS);
        }
    }
Anuncios

En este metodo tenemos un bucle que llamara al metodo actualizar de cada uno de los objetos de Bala creados, lo bueno de trabajar de esta forma es que solamente actualizara el movimiento de la cantidad de balas que fueron generadas, nuestra siguiente modificacion sera en el metodo detectarColisiones con el siguiente codigo:

    private void detectarColisiones(){
        for(int i=0; i < mNumBalas; i++){
            if (mBalas[i].getRect().bottom > mScreenY){
                mBalas[i].invertirVelocidadY();
            } else if (mBalas[i].getRect().top<0){
                mBalas[i].invertirVelocidadY();
            } else if (mBalas[i].getRect().left<0){
                mBalas[i].invertirVelocidadX();
            } else if (mBalas[i].getRect().right > mScreenX){
                mBalas[i].invertirVelocidadX();
            }
        }
    }
Anuncios
Anuncios

En este caso tenemos de vuelta un bucle for que usaremos para que trabaje con las balas generadas en pantalla, por cada bala chequeamos las cuatro propiedades de nuestro rectangulo, en el primer caso verifica si bottom es mayor al alto de la pantalla, en caso de ser cierto llama a invertirVelocidadY, en el segundo caso verifica si top es menor a cero en caso de ser cierto vuelve a llamar a invertirVelocidadY, para el siguiente condicional chequeamos si left es menor a cero pero esta vez llamara a invertirVelocidadX y lo mismo hara si en el siguiente condicional la propiedad right es mayor al ancho de la pantalla.

Con esto ya tenemos una parte definida de la deteccion de colisiones donde evitaremos que las balas se vayan de la pantalla, nuestra siguiente modificacion sera en el metodo dibujar donde al igual que en el post anterior agregaremos este bloque despues que definimos el color de mPincel y antes del condicional para el llamado de imprimirDepuracion, veamos el nuevo bloque:

for(int i=0; i<mNumBalas; i++){
    mCanvas.drawRect(mBalas[i].getRect(),mPincel);
}

Volvemos a utilizar el mismo ciclo pero esta vez si crearemos a la bala porque por medio de drawRect lo dibujaremos en pantalla, esto es lo unico que hace, nuestra siguiente modificacion sera justamente la encargada de generar las balas, para ello modificaremos al metodo generarBalas de la siguiente manera:

    private void generaBalas(){
        int generaX;
        int generaY;
        int velocidadX;
        int velocidadY;

        generaX = mRandomX.nextInt(mScreenX);
        generaY = mRandomY.nextInt(mScreenY);

        velocidadX = 1;
        if (mRandomX.nextInt(2) == 0)
            velocidadX = -1;

        velocidadY = 1;
        if (mRandomY.nextInt(2) == 0)
            velocidadY = -1;

        mBalas[mNumBalas].generar(
                generaX,
                generaY,
                velocidadX,
                velocidadY);
        
        mNumBalas++;
    }
Anuncios
Anuncios

En esta funcion primero declarara cuatro variables, las primeras dos para los ejes X e Y de nuestra bala y las siguientes son para las velocidades sobre los mismos ejes respectivamente, las siguientes dos lineas se encargaran de generar un numero al azar dentro de la pantalla primero para el eje X y luego para el eje Y, asignaremos a velocidadX el valor de uno y el siguiente condicional hara que de forma completamente al azar convierta a velocidadX en negativo, el siguiente condicional hace exactamente lo mismo pero para velocidadY, por ultimo llamamos a generar del objeto Bala y le enviaremos todos los datos que completamos anteriormente, por ultimo incrementamos a mNumBalas, esto le indicara al programa la cantidad actual de balas en pantalla, por ultimo tenemos que modificar a onTouchEvent de la siguiente manera:

    @Override
    public boolean onTouchEvent(MotionEvent evento){
        mPausado = false;
        generaBalas();
        return true;
    }
Anuncios

La primera linea indicara que nuestro juego no esta pausado, luego llama a generaBalas y por ultimo sigue devolviendo true para seguir en este entorno, si lo compilamos y probamos veremos algo como esto

Anuncios

Como pueden ver tenemos una verdadera balacera en pantalla pero antes de terminar vamos a hacer un par de modificaciones, la primera va a ser con respecto a la cantidad de balas en pantalla, vamos a agregar la siguiente constante en las variables de la clase BalaceraJuego pero antes de la creacion de nuestro array llamado Balas:

private final int BALASMAXIMA = 100;
Anuncios

Con esto podemos establecer de una manera simple la cantidad maxima de balas en pantalla, para esta ocasion bajaremos de 10000 a 100, esto va a ser para un verificacion, nuestra siguiente modificacion sera la aplicacion de esta constante y para ello modificaremos el array de la siguiente manera:

private Bala [] mBalas = new Bala[BALASMAXIMA];
Anuncios

Con esto nuestro array siempre tendra el tamaño que definamos en la constante, dando cierto dinamismo pero solo cierto, nuestra siguiente modificacion sera en imprimirDepuracion donde agregaremos el siguiente bloque:

        mCanvas.drawText("mNumBalas: " + mNumBalas,
                10,depuraInicio + (depuraTamano * 2),
                mPincel);
Anuncios

Oops, que pacho? Porque se rompio el juego, si visitamos el log veremos que la excepcion es la siguiente:

java.lang.ArrayIndexOutOfBoundsException: length=100; index=100
Anuncios

En este caso en particular se debio a que nos pasamos con la cantidad de indices y esta supero el tamaño, recuerdan cuando hablamos de arrays dijimos que en caso de pasarnos del tamaño podia ocurrir dos cosas y una era esta, para evitar esto vamos a modificar el metodo generaBalas de la siguiente manera:

    private void generaBalas(){
        if (mNumBalas<BALASMAXIMA)
        {
            int generaX;
            int generaY;
            int velocidadX;
            int velocidadY;

            generaX = mRandomX.nextInt(mScreenX);
            generaY = mRandomY.nextInt(mScreenY);

            velocidadX = 1;
            if (mRandomX.nextInt(2) == 0)
                velocidadX = -1;

            velocidadY = 1;
            if (mRandomY.nextInt(2) == 0)
                velocidadY = -1;

            mBalas[mNumBalas].generar(
                    generaX,
                    generaY,
                    velocidadX,
                    velocidadY);
            mNumBalas++;
        }
    }
Anuncios

En esta situacion hicimos un cambio simple, agregamos un condicional donde verifica que mNumBalas sea menor a BALASMAXIMA y siempre que se cumpla esta condicion ejecutara el codigo de nuestra funcion cuando no se cumpla omitira la ejecucion del codigo, con esto solucionariamos el problema anterior, vamos a probar si es verdad mediante el siguiente video

Anuncios

Antes de finalizar vamos a mostrar como quedo hasta ahora nuestra clase BalaceraJuego:

BalaceraJuego.java

package org.example.balacera;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.util.Random;

class BalaceraJuego extends SurfaceView implements Runnable {

    boolean mDepurando = true;
    private Thread mJuegoTread=null;
    private volatile boolean mJugando;
    private boolean mPausado;
    private SurfaceHolder mNuestroHolder;
    private Canvas mCanvas;
    private Paint mPincel;
    private long mFPS;
    private final int MILES_EN_SEGUNDOS = 1000;
    private int mScreenX;
    private int mScreenY;
    private int mFontTamano;
    private int mFontMargen;
    private SoundPool mSP;
    private int mBeepID = -1;
    private int mTeleportID = -1;
    private final int BALASMAXIMA = 100;
    private Bala [] mBalas = new Bala[BALASMAXIMA];
    private int mNumBalas = 0;
    private int mGeneracionRatio = 1;
    private Random mRandomX = new Random();
    private Random mRandomY = new Random();

    public BalaceraJuego(Context contexto, int x, int y){
        super(contexto);

        mScreenX = x;
        mScreenY = y;
        mFontTamano = mScreenX / 20;
        mFontMargen = mScreenX / 50;
        mNuestroHolder = getHolder();
        mPincel = new Paint();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            AudioAttributes atributos = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .build();
            mSP = new SoundPool.Builder().setMaxStreams(5)
                    .setAudioAttributes(atributos)
                    .build();
        } else {
            mSP = new SoundPool(5, AudioManager.STREAM_MUSIC,0);
        }

        try{
            mBeepID = mSP.load(contexto,R.raw.beep,0);
            mTeleportID = mSP.load(contexto,R.raw.teleport,0);
        } catch (Exception e){
            Log.e("Error","Ha fallado la carga de alguno de los archivos");
        }

        for(int a=0; a < mBalas.length; a++){
            mBalas[a]=new Bala(mScreenX);
        }
        iniciaJuego();
    }

    public void iniciaJuego(){
    }

    private void generaBalas(){
        if (mNumBalas<BALASMAXIMA)
        {
            int generaX;
            int generaY;
            int velocidadX;
            int velocidadY;

            generaX = mRandomX.nextInt(mScreenX);
            generaY = mRandomY.nextInt(mScreenY);

            velocidadX = 1;
            if (mRandomX.nextInt(2) == 0)
                velocidadX = -1;

            velocidadY = 1;
            if (mRandomY.nextInt(2) == 0)
                velocidadY = -1;

            mBalas[mNumBalas].generar(
                    generaX,
                    generaY,
                    velocidadX,
                    velocidadY);
            mNumBalas++;
        }
    }

    @Override
    public void run(){
        while(mJugando){
            long frameInicioTiempo=System.currentTimeMillis();
            if (!mPausado){
                actualizar();
                detectarColisiones();
            }
            dibujar();
            long frameEsteTiempo=System.currentTimeMillis()
                    - frameInicioTiempo;
            if (frameEsteTiempo>=1)
                mFPS = MILES_EN_SEGUNDOS/frameEsteTiempo;
        }
    }

    private void actualizar(){
        for(int i = 0; i < mNumBalas; i++){
            mBalas[i].actualizar(mFPS);
        }
    }

    private void detectarColisiones(){
        for(int i=0; i < mNumBalas; i++){
            if (mBalas[i].getRect().bottom > mScreenY){
                mBalas[i].invertirVelocidadY();
            } else if (mBalas[i].getRect().top<0){
                mBalas[i].invertirVelocidadY();
            } else if (mBalas[i].getRect().left<0){
                mBalas[i].invertirVelocidadX();
            } else if (mBalas[i].getRect().right > mScreenX){
                mBalas[i].invertirVelocidadX();
            }
        }
    }

    private void dibujar(){
        if (mNuestroHolder.getSurface().isValid()){
            mCanvas = mNuestroHolder.lockCanvas();
            mCanvas.drawColor(Color.argb(255,243,111,36));
            mPincel.setColor(Color.argb(255,255,255,255));

            for(int i=0; i<mNumBalas; i++){
                mCanvas.drawRect(mBalas[i].getRect(),mPincel);
            }

            if (mDepurando)
                imprimirDepuracion();
            mNuestroHolder.unlockCanvasAndPost(mCanvas);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent evento){
        mPausado = false;
        generaBalas();
        return true;
    }

    public void pausa(){
        mJugando = false;
        try{
            mJuegoTread.join();
        } catch (InterruptedException e){
            Log.e("Error","Joining el thread");
        }
    }

    public void retomar(){
        mJugando = true;
        mJuegoTread = new Thread(this);
        mJuegoTread.start();
    }

    private void imprimirDepuracion(){
        int depuraTamano = 35;
        int depuraInicio = 150;
        mPincel.setTextSize(depuraTamano);
        mCanvas.drawText("FPS: " + mFPS,
                10,depuraInicio + depuraTamano, mPincel);
        mCanvas.drawText("mNumBalas: " + mNumBalas,
                10,depuraInicio + (depuraTamano * 2),
                mPincel);
    }
}
Anuncios

En resumen, hoy hemos visto como aplicar lo aprendido en Arrays para poder mostrar mas de una bala, hemos visto como aplicarlo, como mejorarlo, como podemos generar un error y a su vez como solucionarlo, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00