Anuncios

Bienvenidos sean a este post, en el post de hoy no solamente continuaremos con la clase GameState sino que agregaremos algunas acciones mas que interesantes.

Anuncios
Anuncios

La primera implementacion sera un almacenador de puntajes altos en nuestro juego, se lo que estan pensando y este es un tema que ya vimos en el juego Balacera pero esta vez haremos que estos puntajes queden de forma permanente y no se pierdan cuando reiniciemos el juego, para utilizarlo haremos que el objeto de tipo SharedPreferences que vimos al final del post anterior, para esto deberemos hacer varias modificaciones y la primera sera agregar el siguiente constructor en la clase GameState:

    GameState(Iniciador ji, Context contexto){
        mIniciador = ji;
        SharedPreferences prefs;
        prefs = contexto.getSharedPreferences(
                "Record",Context.MODE_PRIVATE);
        mEditor = prefs.edit();
        mPuntajeAlto = prefs.getInt("Record",0);
    }
Anuncios
Anuncios

En este constructor recibiremos dos atributos, uno del tipo de la interfaz y por ultimo el que usaremos para nuestro contexto, con el valor recibido en ji lo asignaremos a mIniciador, despues crearemos un objeto de tipo SharedPreferences y a este por medio de contexto le diremos que recupere el valor de la clave asignada como Record, a mEditor le asignaremos el metodo edit el cual nos permitira editarlo pero de esto hablaremos mas adelante, por ultimo asignamos a mPuntajeAlto el valor de Record y si no existe alguno le pasa el valor de cero, con esto terminamos nuestro constructor nuestra siguiente modificacion sera con el siguiente metodo:

    private void finJuego(){
        mGameOver = true;
        mPausado = true;
        if (mPuntaje>mPuntajeAlto){
            mPuntajeAlto = mPuntaje;
            mEditor.putInt("Record",mPuntajeAlto);
            mEditor.commit();
        }
    }
Anuncios
Anuncios

Este sera el metodo encargado de manejar el final de nuestro juego cuando nos quedemos sin vidas, lo primero que hara es poner a mGameOver y mPausado como true, porque se acabo el juego, luego tendra un condicional donde verifica si mPuntaje es mayor que mPuntajeAlto, en caso de ser verdadero le asigne a mPuntajeAlto el valor de mPuntaje y luego modifica el valor de Record en las preferencias y por ultimo usa el commit para modificar el valor, nuestro siguiente paso sera agregar el siguiente metodo:

    void iniciaNuevoJuego(){
        mPuntaje = 0;
        mNumNaves = 3;
        paraDibujar();
        mIniciador.desapareceReaparece();
        retomar();
        iniciaDibujar();
    }
Anuncios

Este metodo lo usaremos para iniciar un nuevo juego, primero inicia a mPuntaje y mNumNaves con los valores cero y tres respectivamente, luego llama a paraDibujar, nos devuelve un error pero no se preocupen ya lo solucionamos, lo siguiente es llamar a desapareceReaparece, luego llama a retomar, por ultimo llamamos a iniciaDibujar, estas dos funciones aun existen pero ya lo solucionaremos, pasemos al siguiente metodo:

    void perderVida(SoundEngine se){
        mNumNaves--;
        se.repJugExplotar();
        if (mNumNaves==0){
            pausar();
            finJuego();
        }
    }
Anuncios
Anuncios

Este metodo como su nombre lo indica es el encargado de monitorear cuando perdemos una nave o una vida, tiene la particularidad de llamar a una clase que todavia no existe, SoundEngine, pero en el proximo post hablaremos sobre ella, lo primero que realiza es la disminucion de la vida, despues llama a un metodo de la clase, por ultimo tiene un condicional donde verifica si mNumNaves es igual a cero, en caso de ser verdadero procede a llamar a pausar y por ultimo al metodo finJuego, para finalizar con nuestra clase vamos a agregar los siguientes metodos:

    int getNumNaves(){
        return mNumNaves;
    }

    void incrementaPuntaje(){
        mPuntaje++;
    }

    int getPuntaje(){
        return mPuntaje;
    }

    int getPuntajeAlto(){
        return mPuntajeAlto;
    }

    void pausar(){
        mPausado = true;
    }

    void retomar(){
        mPausado = false;
        mGameOver = false;
    }

    void detenerTodo(){
        mPausado = true;
        mGameOver = true;
        mThredCorriendo = false;
    }

    boolean getThreadCorriendo(){
        return mThredCorriendo;
    }

    void startThread(){
        mThredCorriendo = true;
    }

    private void detenDibujar(){
        mDibujando = false;
    }

    private void iniciaDibujar(){
        mDibujando = true;
    }

    boolean getDibujando(){
        return mDibujando;
    }

    boolean getPausado(){
        return mPausado;
    }
    
    boolean getGameOver(){
        return mGameOver;
    }
Anuncios
Anuncios

Como pueden ver son unos metodos getter and setter para modificar u obtener los distintos datos de nuestra clase, veamos para que sirve cada uno de acuerdo al orden anterior:

  • Devuelve el valor actual de mNumNaves
  • Incrementa el puntaje del jugador
  • Devuelve el puntaje del jugador
  • Devuelve el puntaje mas del alto del juego
  • Pausa el juego pasando mPausado a true
  • Despausa el juego pasando mPausado y mGameOver a true
  • Detiene completamente el juego
  • Devuelve el valor de mThreadCorriendo
  • Inicia el thread seteando a mThreadCorriendo como true
  • Detiene el dibujar en pantalla seteando a mDibujando como false
  • Inicia el dibujar en pantalla seteando a mDibujando como true
  • Devuelve el estado de mDibujando
  • Devuelve el estado de mPausado
  • Devuelve el estado de mGameOver
Anuncios

Con esto ya tenemos completa, por ahora, a la clase GameState veamos como quedo el codigo final:

GameState.java

package org.example.invasores;

import android.content.Context;
import android.content.SharedPreferences;

final class GameState {
    private static volatile boolean mThredCorriendo = false;
    private static volatile boolean mPausado = true;
    private static volatile boolean mGameOver = true;
    private static volatile boolean mDibujando = false;

    private Iniciador mIniciador;

    private int mPuntaje;
    private int mPuntajeAlto;
    private int mNumNaves;

    private SharedPreferences.Editor mEditor;

    GameState(Iniciador ji, Context contexto){
        mIniciador = ji;
        SharedPreferences prefs;
        prefs = contexto.getSharedPreferences(
                "Record",Context.MODE_PRIVATE);
        mEditor = prefs.edit();
        mPuntajeAlto = prefs.getInt("Record",0);
    }

    private void finJuego(){
        mGameOver = true;
        mPausado = true;
        if (mPuntaje>mPuntajeAlto){
            mPuntajeAlto = mPuntaje;
            mEditor.putInt("Record",mPuntajeAlto);
            mEditor.commit();
        }
    }

    void iniciaNuevoJuego(){
        mPuntaje = 0;
        mNumNaves = 3;
        paraDibujar();
        mIniciador.desapareceReaparece();
        retomar();
        iniciaDibujar();
    }

    void perderVida(SoundEngine se){
        mNumNaves--;
        se.repJugExplotar();
        if (mNumNaves==0){
            pausar();
            finJuego();
        }
    }

    int getNumNaves(){
        return mNumNaves;
    }

    void incrementaPuntaje(){
        mPuntaje++;
    }

    int getPuntaje(){
        return mPuntaje;
    }

    int getPuntajeAlto(){
        return mPuntajeAlto;
    }

    void pausar(){
        mPausado = true;
    }

    void retomar(){
        mPausado = false;
        mGameOver = false;
    }

    void detenerTodo(){
        mPausado = true;
        mGameOver = true;
        mThredCorriendo = false;
    }

    boolean getThreadCorriendo(){
        return mThredCorriendo;
    }

    void startThread(){
        mThredCorriendo = true;
    }

    private void detenDibujar(){
        mDibujando = false;
    }

    private void iniciaDibujar(){
        mDibujando = true;
    }

    boolean getDibujando(){
        return mDibujando;
    }

    boolean getPausado(){
        return mPausado;
    }

    boolean getGameOver(){
        return mGameOver;
    }
}
Anuncios

Para ir finalizando nos resta utilizar esta clase en nuestro Game Engine, para ello debemos hacer algunas modificaciones, la primera va a ser al comienzo de nuestra clase GameEngine donde tenemos las dos variables declaradas agregaremos la siguiente linea:

private GameState mGameState;
Anuncios

Con esto crearemos nuestro objeto, la siguiente modificacion sera en el constructor donde agregaremos la siguiente linea despues de la linea de super:

mGameState = new GameState(this, contexto);
Anuncios

Si recuerdan cuando vimos en el post anterior a la interfaz Iniciador al hacer polimorfismo por medio de interfaces nos permite manejar este tipo directamente, por lo tanto si el constructor espera un atributo de tipo Iniciador con enviarle el this completa la necesidad correctamente dado que son del mismo tipo, luego le pasamos el contexto, con esto ya tenemos nuestro objeto creado, la siguiente modificacion sera en el metodo run donde modificaremos el bloque actual por el siguiente:

    @Override
    public void run(){
        while(mGameState.getThreadCorriendo()){
            long frameInicio = System.currentTimeMillis();
            if (!mGameState.getPausado()){

            }
            long frameActual = System.currentTimeMillis() - frameInicio;
            if (frameActual >= 1){
                final int MILISEGUNDOS = 1000;
                mFPS = MILISEGUNDOS / frameActual;
            }
        }
    }
Anuncios
Anuncios

Para este caso hicimos un par de modificaciones la primera fue que encerramos todo el codigo anterior dentro de un bucle while donde chequea si esta corriendo el thread de GameState, en caso de ser verdadero hara todas las acciones anteriores pero ahora tendra un condicional donde verifica que mPausado sea distinto de true a traves de getPausado y en caso de no estar pausado lo usaremos para actualizar todos los elementos de una manera distinta, el resto hace lo mismo que hablamos en este post, nuestras ultimas dos modificaciones seran con los metodos para manipular los threads en la clase GameEngine, para ello debemos agregar la siguiente linea en el metodo stopThread antes del bloque try/catch:

mGameState.detenerTodo();

En este caso detenemos todos los estado de nuestro juego para “pausarlo” en caso de salir de foco del usuario/jugador, para el metodo startThread vamos a agregar la siguiente linea de volver a definir al thread:

mGameState.startThread();
Anuncios

Basicamente lo unico que es cambiar al estado de mThreadCorriendo antes de comenzarlo realmente, veamos como quedo nuestro codigo finalmente:

GameEngine.java

package org.example.invasores;

import android.content.Context;
import android.graphics.Point;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceView;

class GameEngine extends SurfaceView implements Runnable, Iniciador {
    private Thread mThread = null;
    private long mFPS;

    private GameState mGameState;

    public GameEngine(Context contexto, Point tamano){
        super(contexto);
        mGameState = new GameState(this, contexto);
    }

    @Override
    public void run(){
        while(mGameState.getThreadCorriendo()){
            long frameInicio = System.currentTimeMillis();
            if (!mGameState.getPausado()){

            }
            long frameActual = System.currentTimeMillis() - frameInicio;
            if (frameActual >= 1){
                final int MILISEGUNDOS = 1000;
                mFPS = MILISEGUNDOS / frameActual;
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent evento){
        return true;
    }

    public void stopThread(){
        mGameState.detenerTodo();

        try{
            mThread.join();
        } catch (InterruptedException e){
            Log.e("Exception","stopThread" +
                    e.getMessage());
        }
    }

    public void startThread(){
        mGameState.startThread();
        mThread = new Thread(this);
        mThread.start();
    }

    public void desapareceReaparece(){

    }
}
Anuncios

Con esto ya tenemos bastante avanzado nuestro juego aunque todavia no podemos probarlo porque nos quedo un error pendiente relacionado con la clase SoundEngine pero eso sera un tema que veremos en el proximo post.

Anuncios

En resumen, hoy hemos completado a la clase GameState, hemos creado todos los metodos que nos faltaban, hemos creado los metodos para terminar el juego, iniciar uno nuevo, perder una vida y los metodos getter and setter para manipular las distintas variables por fuera de la clase y tambien hemos implementado esta clase en nuestro GameEngine, espero les haya gustado 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