Anuncios

Bienvenidos sean a este post, en el post de hoy veremos como implementar el bucle de juego que vimos en este post dentro de nuestro segundo proyecto.

Anuncios

En este caso volveremos a nuestra clase PongJuego donde primero agregaremos dos metodos que se encargaran de manipular nuestros threads ya sea para iniciarlo o detenerlo y por medio de estos controlar nuestro bucle de juego, para lograr primero implementaremos a la interfaz Runnable.

Anuncios

Para implementarlo vamos a modificar la siguiente linea:

class PongJuego extends SurfaceView {
Anuncios

De la siguiente manera:

class PongJuego extends SurfaceView implements Runnable {
Anuncios

Cuando hagamos esto nos quedara con un error, esto es debido a la implementacion de la interfaz ya que la misma al no tener definido el metodo run en la misma necesita que sea implementada en la nueva clase, para poder solucionarlo debemos agregar el siguiente bloque dentro de la clase:

    @Override
    public void run(){
        
    }
Anuncios
Anuncios

Con esto solo debera solucionar el error que nos genero anteriormente, nuestro siguiente paso sera el manejo de las fases onPause y onResume que vimos es este post, para ello realizaremos las siguientes modificaciones dentro de la clase PongJuego y para ello primero agregaremos las siguientes declaraciones de variables:

    private Thread mJuegoThread = null;
    private volatile boolean mJugando;
    private boolean mPausado = true;

La primera variable sera el objeto de tipo Thread que usaremos y por ahora solo tendra un valor null, nuestra siguiente linea sera para controlar si estamos jugando o no, el tipo volatile permite que pueda ser accedido tanto por dentro como por fuera del thread y la ultima variable sera para indicar si el juego esta pausado o no, estas dos variables seran las encargadas de controlar nuestro thread, nuestra siguiente modificacion sera los metodos encargados de controlar a este y para ello agregaremos los siguientes metodos en la clase:

    public void pause(){
        mJugando = false;
        try{
            mJuegoThread.join();
        } catch (InterruptedException e){
            Log.e("Error","uniendo a thread");
        }
    }

    public void resume(){
        mJugando = true;
        mJuegoThread = new Thread(this);
        mJuegoThread.start();
    }
Anuncios
Anuncios

El primer metodo sera para pausar a nuestro thread, en este caso primero setearemos a mJugando como false despues usaremos un try/catch para verificar que se ejecute correctamente el metodo join, en caso de fallar escribiremos en el log el error, el siguiente metodo se usa para retomar despues de iniciar la aplicacion o volver de una pausa, dentro del bloque setearemos como true a mJugando, luego construiremos un nuevo thread para finalmente iniciarlo, nuestra siguiente modificacion sera en el metodo run que creamos anteriormente porque si bien nos corrigio el error hasta ahora no cumple ninguna funcion y para ello le agregaremos el siguiente codigo:

    public void run(){
        while(mJugando){
            long frameInicio = System.currentTimeMillis();
            if (!mPausado){
                actualizar();
                detectarColisiones();
            }
            dibujar();
            long esteFrameTiempo = System.currentTimeMillis() - frameInicio;
            if (esteFrameTiempo > 0){
                mFPS = MILES_EN_SEGUNDO / esteFrameTiempo;
            }
        }
    }
Anuncios
Anuncios

En este bloque usaremos un while donde verifica si mJugando es verdadero, luego crearemos una variable de tipo long llamada frameInicio al cual le asignaremos el valor de currentTimeMillis, despues tendremos un condicional donde verifica si mPausado es distinto de verdadero (esto es gracias al negador que tiene adelante) por lo cual en el bloque llamara a dos metodos que aun no existen y por ahora nos devolvera un error, nuestro siguiente paso sera llamar a dibujar, un metodo que creamos anteriormente, el siguiente paso sera crear una variable llamada esteFrameTiempo a la cual le asignaremos la diferencia entre currentTimeMillis y el valor asignado a frameInicio, el siguiente condicional verifica que esteFrameTiempo sea mayor a cero y en caso de ser verdarero asigna a mFPS el valor de la constante MILES_EN_SEGUNDO dividido por el valor de esteFrameTiempo.

Con esto tenemos nuestro metodo run funcionando pero debemos solucionar estos errores y para ello vamos a generar dos nuevos metodos de la siguiente manera:

    private void actualizar(){

    }

    private void detectarColisiones(){

    }
Anuncios

Como pueden ver solamente las definimos en blanco para que el programa tenga una referencia a las mismas pero todavia no haran nada, mas adelante las completaremos, antes de ir finalizando veamos como esta quedando nuestro codigo:

PongJuego.java

package org.example.pong;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

class PongJuego extends SurfaceView implements Runnable {

    private final boolean DEPURANDO = true;

    private SurfaceHolder mNuestroCont;
    private Canvas mCanvas;
    private Paint mPincel;

    private long mFPS;

    private final int MILES_EN_SEGUNDO = 1000;

    private int mScreenX;
    private int mScreenY;

    private int mFontTam;
    private int mFontMargen;

    private Bate mBate;
    private Pelota mPelota;

    private int mPuntaje;
    private int mVidas;

    private Thread mJuegoThread = null;
    private volatile boolean mJugando;
    private boolean mPausado = true;

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

        mScreenX = x;
        mScreenY = y;

        mFontTam = mScreenX / 20;
        mFontMargen = mScreenX /75;

        mNuestroCont = getHolder();
        mPincel = new Paint();

        iniciaNuevoJuego();
    }

    private void iniciaNuevoJuego(){

        mPuntaje = 0;
        mVidas = 3;

    }

    private void dibujar(){
        if (mNuestroCont.getSurface().isValid()){
            mCanvas = mNuestroCont.lockCanvas();
            mCanvas.drawColor(Color.argb(255,26,128,182));
            mPincel.setColor(Color.argb(255,255,255,255));
            mPincel.setTextSize(mFontTam);
            mCanvas.drawText("Puntaje: " + mPuntaje
                            + "  Vidas: " + mVidas,
                    mFontMargen,mFontTam,mPincel);
            if (DEPURANDO){
                imprimirDepuracion();
            }
            mNuestroCont.unlockCanvasAndPost(mCanvas);
        }
    }

    private void imprimirDepuracion(){
        int debugTam = mFontTam / 2;
        int debugComienzo = 150;
        mPincel.setTextSize(debugTam);
        mCanvas.drawText("FPS: " + mFPS,
                10,
                debugComienzo + debugTam,
                mPincel);
    }

    @Override
    public void run(){
        while(mJugando){
            long frameInicio = System.currentTimeMillis();
            if (!mPausado){
                actualizar();
                detectarColisiones();
            }
            dibujar();
            long esteFrameTiempo = System.currentTimeMillis() - frameInicio;
            if (esteFrameTiempo > 0){
                mFPS = MILES_EN_SEGUNDO / esteFrameTiempo;
            }
        }
    }

    private void actualizar(){

    }

    private void detectarColisiones(){

    }

    public void pause(){
        mJugando = false;
        try{
            mJuegoThread.join();
        } catch (InterruptedException e){
            Log.e("Error","uniendo a thread");
        }
    }

    public void resume(){
        mJugando = true;
        mJuegoThread = new Thread(this);
        mJuegoThread.start();
    }
}
Anuncios

Para nuestra ultima modificacion volveremos a la clase PongActivity y modificaremos los metodos onResume y onPause de la siguiente forma:

    @Override
    protected void onResume(){
        super.onResume();
        mPongJuego.resume();
    }

    @Override
    protected void onPause(){
        super.onPause();
        mPongJuego.pause();
    }
Anuncios

Con nuestros nuevos metodos ahora podremos pausar o retomar a nuestro juego cada vez que este pase a segundo plano o vuelva al primer plano, y con esto hemos completado las modificaciones principales para nuestro bucle de juego y como manejar el thread que generamos para este.

Anuncios

En resumen, hoy hemos visto como implementar el bucle de juego, como se hace a traves del thread, como crear los metodos para ello, hemos establecido la base para el corazon de nuestro juego y hemos visto como implementar la interfaz runnable, espero les haya sido util 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