Bienvenidos sean a este post, hoy aplicaremos uno de los pilares de OOP.
Una de las cosas que menciono siempre sobre herencia es, este es uno de los pilares fundamentales de la programacion orientada a objetos u OOP. Pero porque necesitamos herencia? Porque muchos de esos objetos tendran las mismas funciones basicas y casi los mismos datos, estas van a ser algunas de las variables y funciones que se repetiran:
- Casi todos los objetos van a requerir una funcion para dibujar
- Los objetos que se dibujen necesitan variables para saber su ubicacion en los ejes X e Y
- No todos seran estaticos, por lo tanto habra una funcion para actualizar
- Estos objetos seran responsables de limpiarse a si mismos, se necesita una funcion especifica para eso.
Para poder trabajar sobre herencia, vamos a necesitar el codigo que vimos en el post anterior. Sino lo tenes, te dejo un link para descargarlo:
Descarguen el archivo y extraigan el contenido en el PC. Para que este codigo funcione deben revincular a SDL y SDL_Image. Si no saben hacerlo para SDL, les recomiendo este post. Y sino saben sobre SDL_Image, les paso este otro post. Una vez que tengan todo funcionando correctamente, podemos continuar con el tema de hoy.
Lo primero que haremos sera ir al Explorador de soluciones, y dentro de nuestro proyecto Juego agregaremos una nueva clase. Para ello, presionamos boton derecho sobre el programa y seleccionamos Agregar, y luego Clase. Esto nos abrira una nueva ventana y la completaremos con los siguientes datos:
- Nombre de la clase: ObjetoJuego
- Archivo .h: ObjetoJuego.h
- Archivo .cpp: ObjetoJuego.cpp
Nuestro siguiente paso sera agregar el siguiente codigo dentro del archivo ObjetoJuego.h:
ObjetoJuego.h
#pragma once
#ifndef __ObjetoJuego__
#define __ObjetoJuego__
#include <SDL3/SDL.h>
#include <string>
#include "ManejarTexturas.h"
class ObjetoJuego
{
public:
void cargar(float, float, float, float, std::string);
void dibujar(SDL_Renderer*);
void actualizar();
void limpiar();
protected:
std::string m_idTextura;
int m_frameActual;
int m_filaActual;
float m_x;
float m_y;
float m_ancho;
float m_alto;
};
#endif //! defined(__ObjetoJuego__)
En este codigo primero haremos la rutina de chequear si no esta definida nuestra clase y en caso de ser verdadero la carga en memoria, nuestro siguiente paso sera incluir a la libreria string, la de SDL, y la encargada de manejar las texturas. Lo siguiente sera definir la parte publica y protegida de la clase, para la parte publica definiremos los prototipos de la funcion para cargar, para dibujar, para actualizar y para limpiar, tal como hablamos anteriormente. En cambio, en nuestra parte protegida declaramos las variables miembro de la clase, entre las que tendremos las siguientes:
- m_IdTextura: para almacenar el id de la textura
- m_frameActual: almacena el cuadro actual
- m_filaActual: almacena la fila actual
- m_x: almacena el eje X del objeto
- m_y: almacena el eje Y del objeto
- m_ancho: almacena el ancho del objeto
- m_alto: almacena el alto del objeto
Esto es solamente el archivo de encabezado de la clase encargada de los objetos del juego. Pasemos a definir a todas estas funciones y para ello modificaremos a ObjetoJuego.cpp de la siguiente manera:
ObjetoJuego.cpp
#include "ObjetoJuego.h"
void ObjetoJuego::cargar(float x, float y, float ancho, float alto,
std::string idTextura) {
m_x = x;
m_y = y;
m_ancho = ancho;
m_alto = alto;
m_idTextura = idTextura;
m_frameActual = 1;
m_filaActual = 1;
}
void ObjetoJuego::dibujar(SDL_Renderer* pRenderer) {
ManejarTexturas::instanciar()->dibujar_frame(m_idTextura, m_x, m_y,
m_ancho, m_alto, m_filaActual, m_frameActual, pRenderer);
}
void ObjetoJuego::actualizar() {
m_x += 1;
}
void ObjetoJuego::limpiar() {}
En este archivo definiremos los prototipos del encabezado, la primera funcion que definiremos sera cargar y en esta iniciaremos a todos nuestras variables miembros de la parte protegida. Observen que vamos a recibir la posicion de nuestro objeto (x e y), los datos de las dimensiones (ancho y alto) y el Id de la textura, despues con estos mismos datos definiremos las variables pero a m_filaActual y m_frameActual le asignamos nosotros un valor. La siguiente funcion es dibujar y en este caso solo recibiremos un objeto de tipo SDL_Renderer y aca entra en accion la clase manejadora de texturas porque de aqui llamaremos a instanciar, recuerden que es nuestro singleton que vimos anteriormente, y desde este llamaremos a dibujar_frame pero con todos los datos de las variables miembro de la clase ObjetoJuego.
La siguiente funcion es actualizar y en esta solamente incrementaremos el valor de nuestro eje horizontal, es decir X, en uno y por ultimo tenemos la funcion limpiar que por el momento la dejaremos en blanco, dando por concluido con nuestro archivo .cpp. Vayamos a crear una nueva clase de la misma forma que antes pero la llamaremos Jugador.
Esta clase que crearemos sera heredera de la clase ObjetoJuego, vamos a Jugador.h y agreguemos el siguiente codigo:
Jugador.h
#pragma once
#ifndef __Jugador__
#define __Jugador__
#include "ObjetoJuego.h"
class Jugador : public ObjetoJuego
{
public:
void cargar(float, float, float, float, std::string);
void dibujar(SDL_Renderer*);
void actualizar();
void limpiar();
};
#endif /* defined(__Jugador__)*/
Lo primero que haremos sera incluir a nuestra archivo de encabezado de la clase ObjetoJuego y lo siguiente es hacerla heredera de esta. En public, crearemos los prototipos de las funciones que tenemos en ObjetoJuego para redefinirlas. No tenemos las variables miembro porque al ser heredera de la clase antes citada tendra acceso a las mismas. Nuestra siguiente modificacion sera en Jugador.cpp con el siguiente codigo:
Jugador.cpp
#include "Jugador.h"
void Jugador::cargar(float x, float y, float ancho, float alto,
std::string idTextura) {
ObjetoJuego::cargar(x, y, ancho, alto, idTextura);
}
void Jugador::dibujar(SDL_Renderer* pRenderer) {
ObjetoJuego::dibujar(pRenderer);
}
void Jugador::actualizar() {
m_x -= 1;
}
void Jugador::limpiar() {}
Como en el caso anterior, incluimos al archivo de encabezado de la clase. Luego definimos a nuestras respectivas funciones, la unica particularidad es en el caso de cargar y dibujar que ejecutaran a las funciones que definimos en ObjetoJuego. Le pasaremos los datos que recibimos, y actualizar trabajara de forma inversa. Esto hara que se anime en la direccion opuesta, con esto no solo tenemos una clase que nos permite crear objetos para el juego sino tambien una clase heredera para el jugador. Por ultimo, tenemos una definicion de limpiar la cual por ahora no hara nada, pasemos a modificar a la clase Juego.
Primero abramos a Juego.h y agreguemos entre las lineas de include la siguiente:
#include "Jugador.h"
Esta modificacion nos permitira crear objetos del tipo Jugador (obviamente) y Objetojuego. La siguiente modificacion es en la parte privada de la clase donde agregaremos los siguientes dos objetos:
ObjetoJuego m_obj;
Jugador m_jugador;
Como se dan cuenta ahora creamos un objeto de tipo ObjetoJuego (m_obj) y otro de tipo Jugador para el jugador (m_jugador). Nuestra siguiente modificacion sera en Juego.cpp, vamos a la funcion iniciar y agreguemos las siguientes lineas antes del return true:
m_obj.cargar(100, 100, 128, 82, "animado");
m_jugador.cargar(300, 300, 128, 82, "animado");
En este caso establecemos los datos de donde ubicar a nuestros objetos, su tamaño y el ID para identificar a cual usaremos, la siguiente modificacion sera en la funcion renderizar donde la modificaremos de la siguiente manera:
void Juego::renderizar() {
SDL_RenderClear(m_pRenderer);
m_obj.dibujar(m_pRenderer);
m_jugador.dibujar(m_pRenderer);
SDL_RenderPresent(m_pRenderer);
actualizar();
}
En este caso eliminamos la llamada a instanciar y dibujar_frame para reemplazarlos por la funcion dibujar de cada objeto, recuerden que este llamado lo hacemos por medio de esta funcion. Por ultimo, en este archivo debemos modificar la funcion actualizar de la siguiente manera:
void Juego::actualizar(){
m_obj.actualizar();
m_jugador.actualizar();
}
En este caso hacemos que llame a las funciones actualizar de cada objeto. Nuestra ultima modificacion sera en el main.cpp donde modificaremos este bucle while:
while (g_juego->corriendo()) {
g_juego->manejaEventos();
g_juego->renderizar();
}
De la siguiente manera:
while (g_juego->corriendo()) {
g_juego->manejaEventos();
g_juego->actualizar();
g_juego->renderizar();
SDL_Delay(100);
}
En este bloque lo que hacemos es que se actualice la ubicacion de nuestros objetos del juego y a su vez le agregamos un pequeño retardo, con todo esto podemos probar nuestro juego que funcionara de la siguiente manera
En el video podemos ver como se perdio la animacion de nuestros objetos pero ahora tenemos un desplazamiento distinto para cada uno y como cambio todo gradualmente de una mejor forma.
En resumen, hoy hemos creado una clase de base para cada uno de los objetos del juego, hemos creado una clase que es heredera de la anterior, como recibe las funciones y las variables, como podemos redefinirlas, como trabajarlas en las clases anteriores, y como quedo finalmente funcionando por ahora. 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





