Anuncios

Bienvenidos sean a este post, hoy hablaremos sobre un concepto diferente a como veniamos trabajando hasta ahora con los bitmap.

Anuncios

Hasta ahora siempre que trabajamos con graficos en nuestros juegos utilizamos a la clase Bitmap que no solamente representa al grafico sino que tambien mantiene una copia, en el proyecto anterior lo hicimos un poco diferente porque cada instancia de GameObjeto tenia una clase de componente relacionada a los graficos del mismo tipo que contenia Bitmap, aunque tecnicamente sigue siendo lo mismo porque aun necesita una copia apropiada del Bitmap.

Anuncios
Anuncios

Si bien en el proyecto anterior no teniamos una gran cantidad de enemigos, para este si tendremos muchos elementos en pantalla porque debemos repetir multiples imagenes para generar nuestras plataformas, esto puede derivar en desperdicio de memoria, aumentar el consumo de la bateria, que el juego corra mas lento y en dispositivos mas viejos un error de “Out of memory” debido a que nos quedamos sin memoria, para evitar esto vamos a necesitar compartir instancias de Bitmap entre distintos objetos y aqui es donde entran en accion los temas anteriormente vistos como Singleton y HashMap.

Anuncios
Anuncios

En el proyecto anterior y en este hacemos que nuestros objetos tengan distintas especificaciones entre ellas el nombre del Bitmap, en el post anterior hablamos sobre HashMap y comentamos como se debe trabajar con ella y las caracteristicas que nos provee pero no nombramos lo mas importante de este objeto es que nos da la posibilidad de poder almacenar objetos o mejor dicho los constructores de dichos objetos, entonces nosotros podemos almacenar GameObjeto en HashMap y recuperarlos mediante las claves asignadas, y a su vez podemos hacer una clase disponible al resto de las clases gracias a singleton.

Anuncios

Para comenzar vamos a crear una clase Singleton, la cual contendra todas las instancias de Bitmap requeridas por medio de bitmapNombre de las clases de componentes que hablamos anteriormente, vamos a crear la nueva clase con las siguientes caracteristicas:

  • Nombre: BitmapAlmacen
  • Tipo: Class
Anuncios

Una vez creada la clase modificaremos el codigo generado con el siguiente:

package org.example.pepeaventura;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;

import java.util.HashMap;
import java.util.Map;

class BitmapAlmacen {
    private static Map<String, Bitmap> mBitmapMap;
    private static Map<String, Bitmap> mBitmapInversoMap;
    private static BitmapAlmacen mNuestraInstancia;

    static BitmapAlmacen getInstancia(Context contexto){
        mNuestraInstancia = new BitmapAlmacen(contexto);
        return mNuestraInstancia;
    }

    private BitmapAlmacen(Context c){
        mBitmapMap = new HashMap<>();
        mBitmapInversoMap = new HashMap<>();
        agregarBitmap(c, 
                "muerte_visible",
                new PointF(1,1),
                128,
                true);
    }
}
Anuncios
Anuncios

En este caso primero crearemos tres variables, las primeras dos van a ser dos Map para almacenar los bitmaps con una clave y luego una instancia de nuestra propia clase para crear el Singleton, luego tendremos el metodo para conseguir la instancia de nuestra propia clase y por ultimo tenemos el constructor de tipo privado donde iniciaremos a los Map de Bitmap y por ultimo llamaremos a un metodo llamada agregarBitmap pero de este metodo hablaremos un poco mas adelante, vamos a agregar los siguientes metodos a nuestra clase:

    static Bitmap getBitmap(String nombreBitmap){
        if (mBitmapMap.containsKey(nombreBitmap)){
            return mBitmapMap.get(nombreBitmap);
        } else {
            return mBitmapMap.get("muerte_visible");
        }
    }

    static Bitmap getBitmapInverso(String nombreBitmap){
        if (mBitmapInversoMap.containsKey(nombreBitmap)){
            return mBitmapInversoMap.get(nombreBitmap);
        } else {
            return mBitmapInversoMap.get("muerte_visible");
        }
    }
Anuncios
Anuncios

Estos dos metodos sirven para recuperar el Bitmap en base al nombre de este, para ello lo recibimos como atributo, luego usamos un condicional donde verificamos si contiene la clave informada, por medio de containsKey si es verdad procede a devolver el objeto almacenado en la clave en mBitmapMap de lo contrario devuelve al bitmap de “muerte_visible”, esta es el primer metodo, el siguiente metodo es igual pero en lugar de trabajar con Map de Bitmap normal lo hace con la version inversa, con estos dos metodos ya podemos conseguir los Bitmap de nuestros objetos, los dos metodos al ser estaticos podran ser accedidos por todas las clases, agreguemos el metodo faltante de nuestro constructor:

    static void agregarBitmap(Context c,
                              String nombreBitmap,
                              PointF tamanoObjeto,
                              int pixelsPorMetro,
                              boolean necesitaInverso){
        Bitmap bitmap;
        Bitmap bitmapInverso;

        int resId = c.getResources().getIdentifier(nombreBitmap,
                "drawable",
                c.getPackageName());

        bitmap = BitmapFactory.decodeResource(
                c.getResources(),
                resId);

        bitmap = Bitmap.createScaledBitmap(bitmap,
                (int) tamanoObjeto.x * pixelsPorMetro,
                (int) tamanoObjeto.y * pixelsPorMetro,
                false);

        mBitmapMap.put(nombreBitmap,bitmap);

        if (necesitaInverso){
            Matrix matrix = new Matrix();
            matrix.setScale(-1,1);
            bitmapInverso = Bitmap.createBitmap(bitmap,
                    0,0,
                    bitmap.getWidth(),
                    bitmap.getHeight(),
                    matrix,
                    true);
            mBitmapInversoMap.put(nombreBitmap,bitmapInverso);
        }
    }
Anuncios

En este caso recibiremos 5 atributos:

  • c, sera para el contexto de nuestra clase
  • nombreBitmap, el nombre del bitmap que luego usaremos de clave
  • tamanoObjeto, el tamaño del objeto
  • pixelsPorMetro, lo usaremos para escalar el objeto
  • necesitaInverso, es para indicar si necesita un reflejo de la imagen
Anuncios
Anuncios

Luego crearemos dos objetos de tipo Bitmap para almacenar el bitmap al derecho en el primero y al reves en el siguiente, luego tendremos una nueva variable donde almacenaremos el id del recurso que pasaremos a traves de nombreBitmap, una vez obtenido el id pasaremos a cargar a la imagen en el objeto bitmap mediante el decodeResource de BitmapFactory, nuestro siguiente paso sera reescalar la imagen y para ello usaremos al metodo createScaledBitmap y al momento de usar a tamanoObjeto con sus respectivos ejes X e Y lo multiplicaremos con pixelsPorMetro para modificar su tamaño, y por ultimo lo agregamos al Map donde nombreBitmap sera la clave y bitmap sera el objeto que relacionaremos a dicha clave, con esto ya tenemos nuestro bitmap almacenado pero despues de esto tenemos un condicional donde verifica si necesitaInverso es verdadero y en caso de ser cierto ejecutara su bloque, en este bloque crearemos un objeto Matrix y setearemos su escala, luego usaremos a bitmapInverso donde crearemos un Bitmap pero no el escalado porque aprovecharemos el bitmap que creamos anteriormente y ya esta escalado, aunque si lo aplicaremos a matrix para que haga el efecto espejo, por ultimo lo agregamos dentro del Map pero que creamos para los bitmaps inversos, usamos nuevamente a nombreBitmap como clave y a bitmapInverso como valor.

Anuncios

Solo nos resta agregar un solo metodo para poder terminar con nuestra clase, su codigo es el siguiente:

    static void limpiaAlmacen(){
        mBitmapMap.clear();
        mBitmapInversoMap.clear();
    }
Anuncios
Anuncios

Este metodo solamente se encarga de limpiar el contenido de nuestros objetos Map, se estaran preguntando para que limpiar los maps? El tema con nuestros Map es que guarda una copia del Bitmap en memoria y como tenemos muchos objetos en la misma, a medida que acumulemos mas niveles este tamaño se ira incrementando, y como mencionamos al principio podemos quedarnos sin memoria en algunos dispositivos, dado que estos objetos son usados en cada nivel podemos eliminarlos tranquilamente y sino se volvera a cargar en caso de necesidad pero nos evitaremos problemas mayores.

Anuncios

Con este ultimo metodo ya tenemos nuestra clase terminada, veamos como es su codigo final, hasta ahora:

BitmapAlmacen.java

package org.example.pepeaventura;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;

import java.util.HashMap;
import java.util.Map;

class BitmapAlmacen {
    private static Map<String, Bitmap> mBitmapMap;
    private static Map<String, Bitmap> mBitmapInversoMap;
    private static BitmapAlmacen mNuestraInstancia;

    static BitmapAlmacen getInstancia(Context contexto){
        mNuestraInstancia = new BitmapAlmacen(contexto);
        return mNuestraInstancia;
    }

    private BitmapAlmacen(Context c){
        mBitmapMap = new HashMap<>();
        mBitmapInversoMap = new HashMap<>();
        agregarBitmap(c,
                "muerte_visible",
                new PointF(1,1),
                128,
                true);
    }

    static Bitmap getBitmap(String nombreBitmap){
        if (mBitmapMap.containsKey(nombreBitmap)){
            return mBitmapMap.get(nombreBitmap);
        } else {
            return mBitmapMap.get("muerte_visible");
        }
    }

    static Bitmap getBitmapInverso(String nombreBitmap){
        if (mBitmapInversoMap.containsKey(nombreBitmap)){
            return mBitmapInversoMap.get(nombreBitmap);
        } else {
            return mBitmapInversoMap.get("muerte_visible");
        }
    }

    static void agregarBitmap(Context c,
                              String nombreBitmap,
                              PointF tamanoObjeto,
                              int pixelsPorMetro,
                              boolean necesitaInverso){
        Bitmap bitmap;
        Bitmap bitmapInverso;

        int resId = c.getResources().getIdentifier(nombreBitmap,
                "drawable",
                c.getPackageName());

        bitmap = BitmapFactory.decodeResource(
                c.getResources(),
                resId);

        bitmap = Bitmap.createScaledBitmap(bitmap,
                (int) tamanoObjeto.x * pixelsPorMetro,
                (int) tamanoObjeto.y * pixelsPorMetro,
                false);

        mBitmapMap.put(nombreBitmap,bitmap);

        if (necesitaInverso){
            Matrix matrix = new Matrix();
            matrix.setScale(-1,1);
            bitmapInverso = Bitmap.createBitmap(bitmap,
                    0,0,
                    bitmap.getWidth(),
                    bitmap.getHeight(),
                    matrix,
                    true);
            mBitmapInversoMap.put(nombreBitmap,bitmapInverso);
        }
    }

    static void limpiaAlmacen(){
        mBitmapMap.clear();
        mBitmapInversoMap.clear();
    }
}
Anuncios

En resumen, hoy hemos visto como aplicar no solamente un HashMap para nuestro proyecto sino tambien como crear el patron Singleton para nuestra clase y proyecto, hemos visto como podemos aprovechar la practicidad de usar las especificaciones y el Bitmap, por ultimo como evitar problemas de memoria a largo plazo, 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