Anuncios

Bienvenidos sean a este post, continuando con el post anterior en este nos centraremos en dos componentes para el jugador pero tambien haremos uno para los graficos animados.

Anuncios

Vamos a comenzar con esta ultima y para ello vamos a crear una nueva clase con las siguientes caracteristicas:

  • Nombre: ComponenteGraficosAnimados
  • Tipo: Class
Anuncios

Modifiquemos su codigo con el siguiente:

package org.example.pepeaventura;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;

import org.example.pepeaventura.especoj.EspecJuegoObjeto;

class ComponenteGraficosAnimados implements ComponenteGraficos {

    private String mNombreBitmap;
    private Animadora mAnimadora;
    private Rect mSeccionAdibujar;

    @Override
    public void inicializar(Context contexto,
                            EspecJuegoObjeto espec,
                            PointF tamanoObjeto,
                            int pixelsPorMetro){
        mAnimadora = new Animadora(
                tamanoObjeto.y,
                tamanoObjeto.x,
                espec.getNumFrames(),
                pixelsPorMetro);
        float anchoTotal = tamanoObjeto.x
                * espec.getNumFrames();
        mNombreBitmap = espec.getBitmapNombre();
        BitmapAlmacen.agregarBitmap(
                contexto,
                mNombreBitmap,
                new PointF(anchoTotal, tamanoObjeto.y),
                pixelsPorMetro,
                true );
        mSeccionAdibujar = mAnimadora.getTiempoActual(
                System.currentTimeMillis());
    }

    @Override
    public void dibujar(Canvas canvas,
                        Paint pincel,
                        Transformar t,
                        Camara cam) {

        if (t.apuntandoDerecha()
                || t.apuntandoIzquierda()
                || t.getVelocidad() == 0) {
            mSeccionAdibujar = mAnimadora.getTiempoActual(
                    System.currentTimeMillis());
        }

        Rect coordenadasPantalla = cam.mundoApantalla(
                t.getUbicacion().x,
                t.getUbicacion().y,
                t.getTamano().x,
                t.getTamano().y);

        if (t.getMirandoDerecha()){
            canvas.drawBitmap(BitmapAlmacen.getBitmap(mNombreBitmap),
                    mSeccionAdibujar,
                    coordenadasPantalla,
                    pincel);
        } else {
            canvas.drawBitmap(BitmapAlmacen.getBitmapInverso(mNombreBitmap),
                    mSeccionAdibujar,
                    coordenadasPantalla,
                    pincel);
        }
    }
}
Anuncios

Para esta clase vamos a implementar a la interfaz ComponenteGraficos, la cual necesitara de dos metodos para poder ser implementada, lo primero que haremos en el bloque de la clase sera crear tres variables:

  • mNombreBitmap, almacena el nombre del Bitmap
  • mAnimadora, se encarga de la animacion
  • mSeccionAdibujar, define la seccion que usaremos para dibujar la animacion
Anuncios
Anuncios

Despues tenemos al primero de los metodos que necesitamos para implementar la interfaz, en este caso inicializar, para ello recibiremos cuatro atributos y nuestro primer paso en el bloque sera definir a mAnimadora, donde por medio de los ejes X e Y de tamanoObjeto le enviaremos el ancho y alto de la imagen, le pasemos el total de frames y por ultimo la escala que usaremos, el siguiente paso sera crear una variable que usaremos para saber el ancho total de la lamina de animaciones y para ello multiplicaremos el ancho del sprite por la cantidad de frames, despues definiremos a mNombreBitmap, para luego agregar este bitmap a BitmapAlmacen, para lo cual pasamos el contexto, el nombre del bitmap y luego la curiosidad un nuevo objeto de tipo PointF donde pasaremos el ancho total de la lamina y la altura por medio del eje Y de tamanoObjeto, luego la escala y por ultimo informamos que tendra una imagen inversa, por ultimo definimos la seccion a dibujar y en este caso obtendremos el primer frame a dibujar, pasemos al siguiente metodo.

Anuncios

El metodo sera dibujar, para el cual tambien recibiremos cuatro atributos que son el Canvas, el pincel para dibujarlo, la clase para transformar y la camara, nuestro primer paso sera un condicional donde verifica si esta apuntando a la derecha, o a la izquierda o la velocidad es igual a cero, esto nos servira para dibujar un grafico en movimiento o uno que tenga velocidad de cero, por ejemplo el fuego que es animado pero no se mueve, en este caso volvemos a establecer el frame en base al tiempo actual, despues tendremos esta variable:

        Rect coordenadasPantalla = cam.mundoApantalla(
                t.getUbicacion().x,
                t.getUbicacion().y,
                t.getTamano().x,
                t.getTamano().y);
Anuncios

Este sera la variable encargada de las cuatro coordenadas en pantalla, para ello usaremos el metodo mundoApantalla para convertir la seccion donde esta nuestro jugador en el nivel y verlo por medio de la camara, observen como vamos a tener la ubicacion de los ejes X e Y y luego el ancho y el alto de la imagen, despues tendremos un condicional donde verifica si esta mirando a la derecha, en caso de ser verdadero procede a dibujar el bitmap en base a toda la informacion que obtuvimos antes y en este caso usamos a getBitmap de BitmapAlmacen pero en caso de no ser cierto hace exactamente lo mismo pero utiliza a getBitmapInverso para hacer el reflejo del bitmap, con esto ya tenemos cubierta nuestra clase pasemos a la siguiente.

Anuncios

Nuestro siguiente paso sera crear una clase con las siguientes caracteristicas:

  • Nombre: ComponenteEntradaJugador
  • Tipo: Class
Anuncios

Modifiquemos el codigo con el siguiente:

package org.example.pepeaventura;

class ComponenteEntradaJugador implements ObservadorEntrada {

    private Transformar mJugadoresTransformar;
    private JugadorTransformar mJugadorJugadoresTransformar;

    ComponenteEntradaJugador(GameEngine ger){
        ger.addObservador(this);
    }

    public void setTransformar(Transformar transformar){
        mJugadoresTransformar = transformar;
        mJugadorJugadoresTransformar = 
                (JugadorTransformar) transformar;
    }
}
Anuncios

En este caso la haremos heredera de ObservadorEntrada, nuestro primer paso sera crear dos variables de tipo Transformar y JugadorTransformar y si bien ambos son para el jugador pronto sabremos porque enviamos a ambos, lo siguiente es el constructor donde agregaremos el observador, por ultimo tenemos el metodo setTransformar que se encarga de setear a ambas variables, nuestro siguiente paso sera agregar el metodo handleInput para poder implementar la interfaz:

    public void handleInput(MotionEvent evento,
                            GameState gameState,
                            ArrayList<Rect> botones){
        int i = evento.getActionIndex();
        int x = (int) evento.getX(i);
        int y = (int) evento.getY(i);

        if (!gameState.getPausado()){
            switch (evento.getAction() & MotionEvent.ACTION_MASK){
                case MotionEvent.ACTION_UP:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)
                            || botones.get(HUD.DERECHA)
                            .contains(x,y)){
                        mJugadoresTransformar.detenerHorizontal();
                    }
                    break;
                case MotionEvent.ACTION_DOWN:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)){
                        mJugadoresTransformar.apuntarIzquierda();
                    } else if (botones.get(HUD.DERECHA).contains(x,y)){
                        mJugadoresTransformar.apuntarDerecha();
                    } else if (botones.get(HUD.SALTAR).contains(x,y)){
                        mJugadorJugadoresTransformar.iniciaSalto();
                    }
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)
                            || botones.get(HUD.DERECHA)
                            .contains(x,y)){
                        mJugadoresTransformar.detenerHorizontal();
                    }
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)){
                        mJugadoresTransformar.apuntarIzquierda();
                    } else if (botones.get(HUD.DERECHA).contains(x,y)){
                        mJugadoresTransformar.apuntarDerecha();
                    } else if (botones.get(HUD.SALTAR).contains(x,y)){
                        mJugadorJugadoresTransformar.iniciaSalto();
                    }
                    break;
            }
        }
    }
Anuncios

La primera parte es muy similar a cuando hablamos de la UIcontroladora en este post, donde recibiremos los mismos datos y las variables sirven para lo mismo es decir:

  • i, sera para el indice de la accion del evento
  • x, el eje X del evento
  • y, el eje Y del evento
Anuncios

Luego tenemos un condicional donde verifica que el juego no este en estado pausado y en caso de ser verdadero utiliza un switch para chequear que si el boton esta presionado (DOWN) o liberado (UP) y hacer las respectivas acciones, veamos el primer case:

                case MotionEvent.ACTION_UP:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)
                            || botones.get(HUD.DERECHA)
                            .contains(x,y)){
                        mJugadoresTransformar.detenerHorizontal();
                    }
                    break;
Anuncios

Como dijimos el ACTION_UP es cuando sacamos el dedo de la pantalla, y en este caso verifica si se presiono el boton izquierdo o derecho, mediante la verificacion de si contiene a x e y en cada uno de los elementos dentro de botones, en cualquiera de los dos casos si se solto alguno de ellos llama a detenerHorizontal para detener los desplazamientos horizontales, tan simple como eso, veamos el siguiente case:

                case MotionEvent.ACTION_DOWN:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)){
                        mJugadoresTransformar.apuntarIzquierda();
                    } else if (botones.get(HUD.DERECHA).contains(x,y)){
                        mJugadoresTransformar.apuntarDerecha();
                    } else if (botones.get(HUD.SALTAR).contains(x,y)){
                        mJugadorJugadoresTransformar.iniciaSalto();
                    }
                    break;
Anuncios

En este case se ejecutara cuando presionamos la pantalla, y aca tendremos varios if donde primero verifica si x e y estan contenidos en el boton para ir a la izquierda, si es verdad hace que apuntemos a la izquierda, el siguiente condicional verifica si presionamos el boton para ir a la derecha, si es verdad apuntaremos a la derecha, de lo contrario si el boton presionado es el de salto llama a iniciaSalto pero de la variable creada de JugadorTransformar, con esto cubrimos de una forma basica los tres movimientos basicos de Pepe, los otros dos case que quedan son exactamente lo mismo a los descriptos anteriormente pero estan ante la eventualidad de que devuelvan esos valores en lugar de los anteriores, es decir que ACTION_POINTER_UP es igual a ACTION_UP, y ACTION_POINTER_DOWN a ACTION_DOWN, veamos el codigo final de esta clase:

ComponenteEntradaJugador.java

package org.example.pepeaventura;

import android.graphics.Rect;
import android.view.MotionEvent;

import java.util.ArrayList;

class ComponenteEntradaJugador implements ObservadorEntrada {

    private Transformar mJugadoresTransformar;
    private JugadorTransformar mJugadorJugadoresTransformar;

    ComponenteEntradaJugador(GameEngine ger){
        ger.addObservador(this);
    }

    public void setTransformar(Transformar transformar){
        mJugadoresTransformar = transformar;
        mJugadorJugadoresTransformar =
                (JugadorTransformar) transformar;
    }

    public void handleInput(MotionEvent evento,
                            GameState gameState,
                            ArrayList<Rect> botones){
        int i = evento.getActionIndex();
        int x = (int) evento.getX(i);
        int y = (int) evento.getY(i);

        if (!gameState.getPausado()){
            switch (evento.getAction() & MotionEvent.ACTION_MASK){
                case MotionEvent.ACTION_UP:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)
                            || botones.get(HUD.DERECHA)
                            .contains(x,y)){
                        mJugadoresTransformar.detenerHorizontal();
                    }
                    break;
                case MotionEvent.ACTION_DOWN:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)){
                        mJugadoresTransformar.apuntarIzquierda();
                    } else if (botones.get(HUD.DERECHA).contains(x,y)){
                        mJugadoresTransformar.apuntarDerecha();
                    } else if (botones.get(HUD.SALTAR).contains(x,y)){
                        mJugadorJugadoresTransformar.iniciaSalto();
                    }
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)
                            || botones.get(HUD.DERECHA)
                            .contains(x,y)){
                        mJugadoresTransformar.detenerHorizontal();
                    }
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    if (botones.get(HUD.IZQUIERDA).contains(x,y)){
                        mJugadoresTransformar.apuntarIzquierda();
                    } else if (botones.get(HUD.DERECHA).contains(x,y)){
                        mJugadoresTransformar.apuntarDerecha();
                    } else if (botones.get(HUD.SALTAR).contains(x,y)){
                        mJugadorJugadoresTransformar.iniciaSalto();
                    }
                    break;
            }
        }
    }
}
Anuncios

Con esto concluimos la clase, vamos a crear una nueva clase con las siguientes caracteristicas:

  • Nombre: ComponenteActualizaJugador
  • Tipo: Class
Anuncios

Como siempre modificaremos el codigo generado con el siguiente:

package org.example.pepeaventura;

class ComponenteActualizaJugador implements ComponenteActualizar {

    private boolean mEstaSaltando = false;
    private long mTiempoInicioSalto;

    private final long TIEMPO_MAX_SALTO = 400;
    private final float GRAVEDAD = 6;
}
Anuncios

En este caso implementamos a la interfaz ComponenteActualizar, en este caso vamos a crear cuatro variables:

  • mEstaSaltando, sera para saber si esta saltando o no
  • mTiempoInicioSalto, almacenara el tiempo de cuando se inicia el salto
  • TIEMPO_MAX_SALTO, el tiempo maximo que dura el salto
  • GRAVEDAD, sera para simular una gravedad en el juego
Anuncios

En este caso GRAVEDAD actuara como una fuerza invisible que nos empujara hacia abajo cada 6 metros virtuales por segundo, con esto comentado pasemos a agregar el metodo de actualizar:

    public void actualizar(long fps,
                           Transformar t,
                           Transformar jugdaorTransfromar){

        JugadorTransformar jt = (JugadorTransformar) t;
        PointF ubicacion = t.getUbicacion();
        float velocidad = t.getVelocidad();

        if (t.apuntandoIzquierda()){
            ubicacion.x -= velocidad / fps;
        } else if (t.apuntandoDerecha()){
            ubicacion.x += velocidad / fps;
        }

        if (jt.cabezaGolpeada()){
            mEstaSaltando = false;
            jt.manejandoCabezaGolpeada();
        }

        if (jt.saltoIniciado()
                && !mEstaSaltando
                && jt.estaEnTierra() ){
            SoundEngine.playSalto();
            mEstaSaltando = true;
            jt.manejandoSalto();
            mTiempoInicioSalto = System.currentTimeMillis();
        }

        if (!mEstaSaltando){
            ubicacion.y += GRAVEDAD / fps;
        } else if (mEstaSaltando) {
            jt.setNoEnTierra();
            if (System.currentTimeMillis()
                    < mTiempoInicioSalto + (TIEMPO_MAX_SALTO / 1.5f)){
                ubicacion.y -= (GRAVEDAD/1.8) / fps;
            } else if (System.currentTimeMillis()
                    < mTiempoInicioSalto + TIEMPO_MAX_SALTO){
                ubicacion.y += (GRAVEDAD) / fps;
            } else if (System.currentTimeMillis()
                    > mTiempoInicioSalto + TIEMPO_MAX_SALTO){
                mEstaSaltando = false;
            }
        }
        t.actualizaChocador();
    }
Anuncios

Este metodo va a ser complejo pero no tanto, comencemos con la primera parte, crearemos tres variables:

  • jt, sera un objeto para manipular al jugador con JugadorTransformar
  • ubicacion, sera para saber la ubicacion del mismo
  • velocidad, tendra la velocidad del jugador
Anuncios

Despues tendremos este condicional:

        if (t.apuntandoIzquierda()){
            ubicacion.x -= velocidad / fps;
        } else if (t.apuntandoDerecha()){
            ubicacion.x += velocidad / fps;
        }
Anuncios

En este condicional verificamos si estamos apuntando a la izquierda, quiere decir que nos estamos desplazando a la izquierda, por ende lo que haremos es reducir el valor del eje X con la division de velocidad y fps, en caso contrario (en el caso de desplazarnos a la derecha) incrementaremos el valor del eje X con la misma formula.

Anuncios
Nota: Recuerden que el simbolo += o -= es lo mismo que tomar el valor que posee la variable y le incrementamos o decrementamos el valor despues del igual.
Anuncios

Con el condicional anterior haremos la caminata de nuestro personaje, pasemos al siguiente condicional:

        if (jt.cabezaGolpeada()){
            mEstaSaltando = false;
            jt.manejandoCabezaGolpeada();
        }
Anuncios

Con este condicional verificamos si golpeamos la cabeza y en cabeza de ser verdadero estableceremos el salto como falso y llamaremos al metodo para manejar el golpe en la cabeza, pasemos a ver el siguiente condicional:

        if (jt.saltoIniciado()
                && !mEstaSaltando
                && jt.estaEnTierra() ){
            SoundEngine.playSalto();
            mEstaSaltando = true;
            jt.manejandoSalto();
            mTiempoInicioSalto = System.currentTimeMillis();
        }
Anuncios

En este caso verificamos si el salto esta iniciado y no esta saltando y a su vez esta en tierra todavia, porque verificamos de esta forma? Porque asi nos evitamos que el jugador salte en medio del aire o si esta cayendo, en caso de ser cierto reproduciremos el sonido de salto, luego estableceremos al estado de saltar como true y comenzaremos a manejar el salto para luego iniciar a mTiempoInicioSalto con el tiempo actual, pasemos al siguiente condicional.

Nota: Se puede hacer una simple modificacion para tener un doble salto o mas, simplemente permitan saltar cuando no esta en el suelo y agreguen un contador para contar la cantidad de veces que le permitiran.
        if (!mEstaSaltando){
            ubicacion.y += GRAVEDAD / fps;
        } else if (mEstaSaltando) {
            jt.setNoEnTierra();
            if (System.currentTimeMillis()
                    < mTiempoInicioSalto + (TIEMPO_MAX_SALTO / 1.5f)){
                ubicacion.y -= (GRAVEDAD/1.8) / fps;
            } else if (System.currentTimeMillis()
                    < mTiempoInicioSalto + TIEMPO_MAX_SALTO){
                ubicacion.y += (GRAVEDAD) / fps;
            } else if (System.currentTimeMillis()
                    > mTiempoInicioSalto + TIEMPO_MAX_SALTO){
                mEstaSaltando = false;
            }
        }
        t.actualizaChocador();
    }
Anuncios

Este condicional se encargara de verificar si no esta saltando, es decir que esta cayendo, entonces incrementaremos el eje Y en base a la division de la gravedad por los fps, en caso contrario (en el caso de estar saltando) primero setearemos que no esta en tierra, luego verificamos si el tiempo actual es menor a la suma del tiempo inicial del salto con el resultado de la division del tiempo maximo del salto por 1.5, en este caso disminuiremos el valor del eje Y con el resultado de la division de GRAVEDAD con 1.8 y a su vez con los fps.

Anuncios

El siguiente condicional chequea si el tiempo actual es menor a la suma del tiempo de inicio del salto con el tiempo maximo del salto, en caso de ser cierto comenzaremos el proceso de caida de nuestro personaje incrementando el eje Y con la division de GRAVEDAD y los fps, por ultimo tenemos un condicional donde verifica que el tiempo actual es mayor al tiempo de inicio del salto con el valor del tiempo maximo del salto, quiere decir que no esta saltando por lo tanto lo setea como falso y sale de todos los condicionales, se hayan cumplido las condiciones procedemos a actualizar los chocadores para saber si hay colisiones o no, con esto concluimos esta clase veamos su codigo final:

ComponenteActualizaJugador.java

package org.example.pepeaventura;

import android.graphics.PointF;

class ComponenteActualizaJugador implements ComponenteActualizar {

    private boolean mEstaSaltando = false;
    private long mTiempoInicioSalto;

    private final long TIEMPO_MAX_SALTO = 400;
    private final float GRAVEDAD = 6;

    public void actualizar(long fps,
                           Transformar t,
                           Transformar jugdaorTransfromar){

        JugadorTransformar jt = (JugadorTransformar) t;
        PointF ubicacion = t.getUbicacion();
        float velocidad = t.getVelocidad();

        if (t.apuntandoIzquierda()){
            ubicacion.x -= velocidad / fps;
        } else if (t.apuntandoDerecha()){
            ubicacion.x += velocidad / fps;
        }

        if (jt.cabezaGolpeada()){
            mEstaSaltando = false;
            jt.manejandoCabezaGolpeada();
        }

        if (jt.saltoIniciado()
                && !mEstaSaltando
                && jt.estaEnTierra() ){
            SoundEngine.playSalto();
            mEstaSaltando = true;
            jt.manejandoSalto();
            mTiempoInicioSalto = System.currentTimeMillis();
        }

        if (!mEstaSaltando){
            ubicacion.y += GRAVEDAD / fps;
        } else if (mEstaSaltando) {
            jt.setNoEnTierra();
            if (System.currentTimeMillis()
                    < mTiempoInicioSalto + (TIEMPO_MAX_SALTO / 1.5f)){
                ubicacion.y -= (GRAVEDAD/1.8) / fps;
            } else if (System.currentTimeMillis()
                    < mTiempoInicioSalto + TIEMPO_MAX_SALTO){
                ubicacion.y += (GRAVEDAD) / fps;
            } else if (System.currentTimeMillis()
                    > mTiempoInicioSalto + TIEMPO_MAX_SALTO){
                mEstaSaltando = false;
            }
        }
        t.actualizaChocador();
    }
}
Anuncios

En resumen, hoy hemos visto tres clases enfocadas mayoritariamente en nuestro jugador, la primera si bien esta pensada para el jugador en realidad es para controlar todos los objetos animados en el juego, luego hicimos una clase que se encargara de controlar las entradas del jugador, es decir cuando toquemos algunos de los botones, por ultimo hemos creado la clase que se encargara de actualizar todo lo relacionado con nuestro jugador, 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.

Donación

Es para mantenimento del sitio, gracias!

$1.00

Anuncio publicitario