Bienvenidos sean a este post, hoy hablaremos sobre un tema particular y muy recurrente en los videojuegos
Este es denominado como el Singleton, pero que es un singleton?
El singleton es la capacidad de una clase que nos dara una sola cosa de algo y lo mas importante que nos garantizara solo ese de algo
El Tinchicus
Esto habitualmente se logra por medio de la utilizacion de una instancia hacia dicha clase, si bien en el post anterior creamos un mecanismo encargado de manejar las imagenes o texturas de nuestro juego todavia necesitamos encontrar una forma de reutilizarlo pero que no sea miembro de la clase Juego porque sino deberemos pasarlo a la funcion encargada de dibujar. Aca es donde entra en accion el patron singleton como una opcion interesante.
Como dijimos antes el singleton solo permite una instancia y esto nos viene genial para nuestra clase ManejarTexturas porque debemos garantizarnos de usar siempre el mismo en todo el juego. Para ello necesitaremos el codigo que modificamos en el post anterior. Sino lo poseen les dejo un link para descargarlo:
Descarguen el archivo y extraigan el contenido del mismo en el PC. Este no funcionara de primera porque necesitan revincular a SDL si no saben como hacerlo les recomiendo este post, y tambien SDL_Image donde al igual que el caso anterior si no saben como hacerlo, les recomiendo visitar este post. Cuando tengan todo funcionando, pasaremos a hacer una serie de modificaciones para poder implementarlo y la primera de ellas sera en el archivo de encabezado, ManejarTexturas.h, donde agregaremos a nuestro constructor en la parte privada de la clase:
ManejarTexturas();
No borren la variable que se encuentra ahi, simplemente agreguen el prototipo del constructor. Esto nos asegura que los objetos solo pueden ser creados por medio de una funcion y no directamente, pero la definiremos en un momento. Lo siguiente sera agregar el siguiente prototipo en la parte publica:
static ManejarTexturas* instanciar();
Esta funcion sera estatica y nos garantiza que estara disponible para todas las clases y demas, segundo sera en un puntero de memoria, tercero el tipo de dato de la funcion debe ser el mismo que de nuestra clase, esto es debido a que devolvemos un objeto del mismo tipo que la clase, pero este lo definiremos en un momento.
La siguiente modificacion en el archivo de encabezado, sera la definicion de la variable e_pInstancia y para ello la agregaremos tambien en la parte publica de la clase:
static ManejarTexturas* e_pInstancia;
Este sera simplemente una declaracion de la misma pero nos servira para almacenar el puntero de cada instancia que generemos. Nos resta una modificacion mas, debemos agregar la siguiente linea:
typedef ManejarTexturas Elmanejador;
Pero esta ira como ultima linea por fuera de #endif, para que se hagan una idea les debe quedar como se ve en la siguiente imagen

Con esto ya terminamos en la parte del archivo de encabezado, pasemos a ManejarTexturas.cpp donde debemos hacer varias modificaciones. Siendo la primera en definir a nuestra variable e_pInstancia y para ello agregaremos la siguiente linea:
ManejarTexturas* ManejarTexturas::e_pInstancia = 0;
Como toda variable estatica siempre deben tener un valor inicial, nuestra siguiente modificacion sera agregar la definicion de la funcion instanciar:
ManejarTexturas* ManejarTexturas::instanciar() {
if (e_pInstancia == 0) {
e_pInstancia = new ManejarTexturas();
return e_pInstancia;
}
return e_pInstancia;
}
Lo primero que tenemos en la funcion es un condicional donde verifica si la variable e_pInstancia es igual a cero, esto implica que no existe ningun objeto o instancia de este, como no tiene ningun valor consideramos que no se creo el objeto de ManejarTexturas por lo cual procedemos a llamar al constructor. Recuerden que el constructor esta en la parte privada y solo puede ser creado de esta manera, lo asignamos a la variable y luego lo devolvemos.
La proxima vez que volvamos a llamar a Instanciar, volvera a chequear si e_pInstancia es igual a cero pero si ya pasamos por esta funcion tendra un valor por lo que ignorara este condicional y devolvera el objeto de e_pInstancia. Es decir, que trabajara siempre con el mismo sector de memoria u objeto. Con esto solo ya nos garantizamos a nuestro singleton pero las modificaciones todavia no terminan. Debemos pasar a definir a nuestro constructor y para ello agregaremos el siguiente codigo:
ManejarTexturas::ManejarTexturas() {}
Simplemente la definimos en blanco porque no necesitamos hacer ninguna modificacion mas o iniciar algo. Con esto ya tenemos realizadas todas las modificaciones necesarias para poder usar el patron singleton. Vamos a realizar una serie de modificaciones en Juego.cpp y la primera de ellas sera comentar o eliminar el siguiente bloque en la funcion iniciar:
m_manejarTexturas.cargar("assets/animate-alpha.png", "animado", m_pRenderer);
Este era el bloque encargado de cargar nuestra imagen y ahora lo reemplazaremos de la siguiente forma:
if (!Elmanejador::instanciar()->cargar("assets/animate-alpha.png",
"animado",m_pRenderer)) {
return false;
}
Este condicional se disparara solamente si falla al cargar a traves de Instanciar, esto por el negador delantero, y si es verdad que no se cumplio con la carga tambien procedemos a devolver un false pero desde la funcion iniciar. En cambio, si se cargo bien este condicional no se cumple, se procede con la carga de la imagen y continuara con la funcion normalmente. La siguiente modificacion sera en la funcion renderizar donde comentaremos o eliminaremos las dos llamadas a dibujar y dibujar_frame:
m_manejarTexturas.dibujar("animado", 0, 0, 128, 82, m_pRenderer);
m_manejarTexturas.dibujar_frame("animado", 100, 100, 128, 82, 1,
m_frameActual, m_pRenderer);
Y agregaremos las siguientes dos llamadas:
Elmanejador::instanciar()->dibujar("animado", 0, 0, 128, 82, m_pRenderer);
Elmanejador::instanciar()->dibujar_frame("animado", 100, 100, 128, 82, 1,
m_frameActual, m_pRenderer);
Nuevamente usamos el identificador para la clase ManejarTexturas, Elmanejador, para llamar a Instanciar, y desde esta llamamos a dibujar con los mismos datos de antes. Despues hacemos lo mismo pero con la funcion dibujar_frame, con los mismos datos que nos manejamos anteriormente. Nuestra ultima modificacion sera ir a Juego.h donde en la parte privada, eliminaremos o comentaremos la linea que creaba el objeto para poder acceder a todas las instancias de la clase ManejarTexturas
ManejarTexturas m_manejarTexturas;
Esto debemos hacerlo porque recuerden que el constructor ahora es privado y no podemos acceder directamente mediante este metodo, por ende nos puede generar errores que no necesitamos a la hora de compilarlo. Con esto realizado podemos proceder a compilar y probar nuestro juego, se debera ver como vimos anteriormente en el post anterior.
Como pueden ver logramos el mismo objetivo pero en esta ocasion siempre trabajaremos en el mismo sector de memoria sin el inconveniente de duplicar nuestro objetos sin necesidad. Parece complejo pero no lo es tanto y a medida que vayamos adentrandonos en este curso tambien iremos ganando en complejidad y deberemos evitar inconvenientes con la memoria.
En resumen, hoy hemos visto el concepto de lo que es singleton, lo escucharan en otros lugares a la hora de programar videojuegos, en que nos puede ser util, hemos visto una forma simple de implementar, lo hemos practicado en nuestro codigo, cuales son algunos de sus beneficios y como nos ayudara en un futuro no muy lejano. Espero les haya sido de utilidad y les dejo un link a GitHub donde estan los codigos creados hoy:
Les dejo algunas de mis redes sociales para seguirme o recibir una notificacion cada vez que subo un nuevo post:


Donación
Es para mantenimento del sitio, gracias!
$1.50





