Bienvenidos sean a este post, hoy veremos como usar clases.
En el dia de hoy, estableceremos la base de nuestro primer «juego» e iremos un poco mas adelante de lo visto hasta el post anterior.
Para ello, tomaremos el concepto visto en el post anterior y lo crearemos en uno nuevo. Para ello, vamos a ir a Visual Studio y crearemos un nuevo proyecto con las siguientes caracteristicas:
- Tipo de Lenguaje: C++
- Plataforma: Windows
- Tipo de Proyecto: Consola
- Plantilla: Proyecto vacío
- Nombre del proyecto: Juego
- Ubicacion: Dejan la predeterminada
- No tildar la opcion de ubicar la solucion y proyecto en el mismo directorio
- Nombre de la solucion: Juego
Una vez que generamos nuestro nuevo proyecto vamos a agregar un archivo nuevo para nuestro codigo. Para ello iremos al Explorador de Soluciones, haran click con el boton sobre el proyecto y seleccionaremos Agregar -> Nuevo Elemento, y nos aparecera el siguiente cuadro

Aqui podemos crear directamente el archivo con la extension .cpp. Pero si este no fuera el caso, podemos presionar Mostrar todas las plantillas y nos cambiara a la siguiente ventana

En este caso, seleccionaremos Archivo C++ (.cpp) y le cambiaremos el nombre a main.cpp. Una vez seleccionado y modificado presionamos Aceptar para crearlo. El siguiente paso sera crear una nueva clase y para ello volvemos a hacer click con el boton derecho sobre el proyecto, seleccionamos Agregar -> Clase lo cual hara aparecer el siguiente cuadro

Nuestro siguiente paso sera completar los campos que necesitemos:
- Nombre de la clase: Juego
- Archivo .h: Juego.h
- Archivo .cpp: Juego.cpp
Debera quedar de la siguiente manera

Con todo esto ya tenemos a nuestros dos archivos creados, quedando de la siguiente manera

Antes de realizar la primera modificacion, tenemos que repetir todo el procedimiento comentado en este post para agregar nuestra libreria y demas objetos necesarios para poder acceder a SDL. Una vez realizado todo los pasos y habilitado SDL pasaremos a Juego.h donde tendremos el siguiente codigo generado:
Juego.h
#pragma once
class Juego
{
};
Esta es la estructura base de nuestra clase que como mencionamos es generada automaticamente, nuestro siguiente paso sera modificarlo de la siguiente manera:
#pragma once
#ifndef __Juego__
#define __Juego__
#include <SDL3/SDL.h>
class Juego
{
public:
Juego() {}
~Juego() {}
bool iniciar(const char*, int, int, int);
void renderizar();
void actualizar();
void manejaEventos();
void limpiar();
bool corriendo();
private:
SDL_Window* m_pVentana;
SDL_Renderer* m_pRenderer;
bool m_bCorriendo;
};
#endif /* defined(__Juego__) */
Nota:
La palabra #ifndef, #define y #endif, nos sirven para evitar que este archivo se cargue varias veces de forma innecesaria.
Tomando el concepto del post anterior y en la seccion publica definimos nuestro constructor predeterminado y destructor. Luego tendremos los prototipos de las funciones que mencionamos en el ciclo de juego, los cuales pronto definiremos, junto al prototipo de una funcion que se encargara devolver el valor de una variable que se encuentra en la parte privada. Con esto completamos la parte publica. En la parte privada, crearemos dos punteros y una variable. Los punteros seran los utilizados para crear y renderizar la ventana respectivamente y la variable la usaremos para saber si el juego esta corriendo o no. Este seria el codigo de nuestro archivo de encabezado o header, pasemos a definir las funciones a traves de Juego.cpp que tenemos creado. Para ello, agregaremos el siguiente codigo:
Juego.cpp
#include "Juego.h"
#include <Windows.h>
bool Juego::iniciar(const char* titulo, int ancho, int alto, int flags) {
if (SDL_Init(SDL_INIT_EVENTS) != 0) {
OutputDebugStringA("SDL_Init iniciado\n");
m_pVentana = SDL_CreateWindow(titulo, ancho, alto, flags);
if (m_pVentana != 0) {
OutputDebugStringA("Ventana Creada\n");
m_pRenderer = SDL_CreateRenderer(m_pVentana, NULL);
if (m_pRenderer != 0) {
OutputDebugStringA("Render Exitoso\n");
SDL_SetRenderDrawColor(m_pRenderer, 255, 255, 255, 255);
}
else {
OutputDebugStringA("Render Fallido\n");
return false;
}
}
else {
OutputDebugStringA("Ventana no Creada\n");
return false;
}
}
else {
OutputDebugStringA("Inicio Fallido\n");
return false;
}
OutputDebugStringA("Inicio Exitoso\n");
m_bCorriendo = true;
return true;
}
void Juego::renderizar() {
SDL_RenderClear(m_pRenderer);
SDL_RenderPresent(m_pRenderer);
}
void Juego::manejaEventos() {
SDL_Event evento;
if (SDL_PollEvent(&evento)) {
switch (evento.type) {
case SDL_EVENT_QUIT:
m_bCorriendo = false;
break;
default:
break;
}
}
}
void Juego::actualizar() {}
void Juego::limpiar() {
OutputDebugStringA("Limpiando Memoria\n");
SDL_DestroyWindow(m_pVentana);
SDL_DestroyRenderer(m_pRenderer);
SDL_Quit();
}
bool Juego::corriendo() { return m_bCorriendo; }
En este archivo tendremos basicas las definiciones de los prototipos que declaramos en el archivo de encabezado pero lo primero que hacemos es incluir al archivo de encabezado (Juego.h) y luego incluimos a Windows.h para tener acceso a una funcion para poder mostrar texto en la salida de Depuracion. Esto lo veremos un poco mas adelante, esto lo hago por un tema de comodidad y practicidad pero queda a criterio de cada uno. Continuando, veamos la primera funcion:
bool Juego::iniciar(const char* titulo, int ancho, int alto, int flags) {
if (SDL_Init(SDL_INIT_EVENTS) != 0) {
OutputDebugStringA("SDL_Init iniciado\n");
m_pVentana = SDL_CreateWindow(titulo, ancho, alto, flags);
if (m_pVentana != 0) {
OutputDebugStringA("Ventana Creada\n");
m_pRenderer = SDL_CreateRenderer(m_pVentana, NULL);
if (m_pRenderer != 0) {
OutputDebugStringA("Render Exitoso\n");
SDL_SetRenderDrawColor(m_pRenderer, 255, 255, 255, 255);
}
else {
OutputDebugStringA("Render Fallido\n");
return false;
}
}
else {
OutputDebugStringA("Ventana no Creada\n");
return false;
}
}
else {
OutputDebugStringA("Inicio Fallido\n");
return false;
}
OutputDebugStringA("Inicio Exitoso\n");
m_bCorriendo = true;
return true;
}
En esta funcion tenemos las rutinas que vinimos viendo en los posts anteriores de como iniciar a SDL. Por medio de un condicional verificamos que se haya iniciado, en caso de ser verdadero crea la ventana con los datos que recibe. Chequea que se haya creado y en caso de ser verdadero procede a renderizarla, la unica diferencia va a estar ahora que en cada uno de los condicionales tendremos un else donde devolvera un mensaje indicando cual fallo. Ya sea el inicio, la creacion de la ventana o la renderizacion y en todos los casos devolvera un false para indicar la falla. En caso de que nada haya fallado tambien veremos una notificacion en cada uno de los casos y al final un mensaje de inicio exitoso. Si observan en todos los casos cuando mostramos un mensaje de exito o fracaso lo hacemos por medio de OutputDebugStringA, el cual se encargara de mostrar los mensajes en la salida de depuracion. Con esto comentado, lo siguiente es pasar el valor de m_bCorriendo a true y devuelve un true. Veamos la siguiente funcion:
void Juego::renderizar() {
SDL_RenderClear(m_pRenderer);
SDL_RenderPresent(m_pRenderer);
}
En esta funcion haremos lo mismo que antes, es decir primero limpiamos el contenido de la ventana o la renderizacion, y luego lo volvemos a crear, cortita y al pie, veamos la siguiente funcion:
void Juego::manejaEventos() {
SDL_Event evento;
if (SDL_PollEvent(&evento)) {
switch (evento.type) {
case SDL_EVENT_QUIT:
m_bCorriendo = false;
break;
default:
break;
}
}
}
Esta es la funcion encargada de manejar los eventos, lo primero que haremos es crear una variable de tipo SDL_Event. Lo siguiente es un condicional donde esta se encarga de verificar si ocurrio algun evento. Luego tenemos al switch donde iremos agregando todos los eventos que deseemos monitorear, para este codigo solo tenemos uno que es cuando se presiona el boton de cierre de la ventana. En caso de que hagamos eso procede a establecer a m_bCorriendo como false, con esto le decimos al programa que el juego ya no esta corriendo, y por ultimo tenemos esta ultima funcion:
void Juego::limpiar() {
OutputDebugStringA("Limpiando Memoria\n");
SDL_DestroyWindow(m_pVentana);
SDL_DestroyRenderer(m_pRenderer);
SDL_Quit();
}
Este sera el metodo encargado de limpiar de memoria todo lo que creamos. En este caso primero muestra un mensaje de lo que esta haciendo. Luego destruye la ventana de memoria, para despues destruir el render de la misma, y finalmente eliminar a SDL de la memoria. Solo nos resta esta funcion:
bool Juego::corriendo() { return m_bCorriendo; }
Esta sera la encargada de informar si el juego sigue corriendo o no. Es decir, la mas importante pero eso lo veremos en un instante. Tambien tenemos definida a la funcion actualizar pero por el momento la dejaremos en blanco. Con esto ya tenemos cubierta a nuestra clase externa del programa. Para nuestro siguiente paso sera agregar el siguiente codigo dentro de main.cpp:
main.cpp
#include "Juego.h"
Juego* g_juego = 0;
int main(int argc, char* argv[]) {
g_juego = new Juego();
g_juego->iniciar("Juego", 320, 240, SDL_WINDOW_OPENGL);
while (g_juego->corriendo()) {
g_juego->manejaEventos();
g_juego->renderizar();
}
g_juego->limpiar();
return 0;
}
Para este archivo, lo primero que haremos es incluir a Juego.h, el otro no es necesario porque esta relacionado a este, despues creamos el objeto de la clase Juego. En el cuerpo del main, definimos con el constructor new a g_juego, el siguiente paso sera usar a iniciar para crear y renderizar la ventana con todos los datos necesarios. Luego vendra un bucle while donde verificara el estado de m_bCorriendo por medio de corriendo, recuerden que la variable es privada pero la funcion no, donde mientras se cumpla la condicion ejecuta manejaEventos y renderizar. Para una vez que no se cumpla la condicion llama a limpar para liberar la memoria y devolver cero para indicar que esta todo bien, con esto podemos pasar a compilarlo y probarlo como se ve en el siguiente video
En el video podemos ver como funciona ahora nuestro «juego», como crea la ventana y vemos en la depuracion los mensajes que posee cada funcion.
En resumen, hoy hemos creado nuestro primer proyecto con clases, como crear un proyecto vacio, como agregar el archivo de codigo y de clases, como se utiliza el archivo de encabezado, como se definen los prototipos del archivo de encabezado, como se implementan en el codigo del programa. Espero les haya sido de utilidad y les dejo un link a GitHub donde estan los codigos creados hoy:
Tambien 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





