I am trying SDL2 Gamedev for the first time so I don't know the best practices yet. Now, I have a code structure which looks something like this:
- main.cpp
- ui
- button.cpp and button.hpp
- objects.cpp and objects.hpp
- rectangle.cpp and rectangle.hpp
- shapes.cpp and shapes.hpp
The shapes class is the base class for all the render-able objects (for example Rects, buttons, etc) and the object class holds all those objects in a std::vector. Now a problem arises when I try to append an object into the vector The append logic:
- if the object has a unique ID which is not yet in the vector push the element into the vector
- else if the unique ID is there in the vector do nothing and return false
When I add all unique elements to the vector the renderer renders everything as it should. But when it finds a duplicate it does not call the push function and as a result the window goes black.
This is my objects.cpp:
#include "objects.hpp"
Objects::Objects()
{
}
Objects::~Objects()
{
// No need to explicitly clear the container; the unique_ptrs will be cleaned up automatically
}
bool Objects::addShapeObject(std::unique_ptr<Shape> shape)
{
for (const auto& existingShape : this->shapesObjectContainer)
{
if (shape->getUID() == existingShape->getUID())
{
printf("Can't add duplicate OBJECTS\nERR: Same UID ->>> %s\n", shape->getUID().c_str());
return false; // Duplicate found, return false
}
}
this->shapesObjectContainer.push_back(std::move(shape));
printf("Was able to push object\n");
return true; // Successfully added
}
void Objects::updateObjects(SDL_Renderer* renderer)
{
for (const auto& shape : this->shapesObjectContainer)
{
shape->update(renderer); // Use smart pointer access
}
}
And here is my buttons.cpp:
#include "buttons.hpp"
#include <random>
#include <ctime>
Button::~Button() {
if (textSurface) {
SDL_FreeSurface(textSurface);
textSurface = nullptr; // Avoid dangling pointer
}
if(textTexture)
{
SDL_DestroyTexture(textTexture);
textTexture = nullptr;
}
if(renderer)
{
SDL_DestroyRenderer(renderer);
renderer = nullptr;
}
if (tFont) {
TTF_CloseFont(tFont);
tFont = nullptr; // Avoid dangling pointer
}
TTF_Quit();
}
Button::Button(int coords[4], int colorRGB[3], SDL_Renderer *gRenderer, std::string UID, const char textString[])
: Rectangle(coords[0], coords[1], coords[2], coords[3], UID),UniqueIdentifier(UID),renderer(gRenderer),textDest ({coords[0],coords[1],coords[2],coords[3]})
{
this->setColor(colorRGB[0],colorRGB[1],colorRGB[2]);
if(TTF_Init() == -1)
{
printf("TTF_Init: %s\n", TTF_GetError());
}
this->tFont = TTF_OpenFont("ui/resources/default.ttf",24);
if(!tFont)
{
printf("Can not load the TTF file \n");
}
else
{
this->foregroundColor = {0x30,0x30,0x30};
this->textString = textString;
this->textSurface = TTF_RenderText_Solid(this->tFont, textString, this->foregroundColor);
if(!this->textSurface)
{
printf("Could not initilized text surface");
}
this->textTexture = SDL_CreateTextureFromSurface(gRenderer,this->textSurface);
}
this->defaultColor = this->getColor();
this->defColorArr[0] = defaultColor[0];
this->defColorArr[1] = defaultColor[1];
this->defColorArr[2] = defaultColor[2];
}
void Button::update(SDL_Renderer *renderer)
{
this->draw(renderer);
printf("Rendering for UID %s \n",this->UniqueIdentifier.c_str());
SDL_RenderCopy(renderer,this->textTexture, NULL ,&(this->textDest));
}
void Button::clickHandler(SDL_Event e, std::function<void()> caller)
{
int X,Y = 0;
SDL_GetMouseState(&X,&Y);
if(X >= this->x && X <= this->x + this->w && Y >= this->y && Y <= this->y + this->h)
{
if(e.type == SDL_MOUSEBUTTONDOWN)
{
if (caller != nullptr) {
caller(); // Call the function
}
this->setColor(0x62,0x62,0x72);
}
else
{
this->setColor(0x92,0x92,0x92);
}
}
else
{
this->setColor(this->defColorArr[0],this->defColorArr[1],this->defColorArr[2]);
}
}
void Button::setText(char textString[])
{
this->textSurface = TTF_RenderText_Solid(this->tFont, textString, this->foregroundColor);
this->textTexture = SDL_CreateTextureFromSurface(this->renderer,this->textSurface);
this->textString = textString;
}
std::string Button::getUID()
{
return std::string(this->UniqueIdentifier);
}
void Button::setText(std::string textString)
{
this->textSurface = TTF_RenderText_Solid(this->tFont, textString.c_str(), this->foregroundColor);
this->textTexture = SDL_CreateTextureFromSurface(this->renderer,this->textSurface);
this->textString = textString;
}
std::string Button::getText()
{
return this->textString;
}
And my rectangle.cpp:
#include "rectangle.hpp"
// Constructor definition
Rectangle::Rectangle(int x, int y, int w, int h,std::string UID):UniqueIdentifier(UID)
,x(x), y(y), w(w), h(h) {
}
// Method to set the color of the rectangle
void Rectangle::setColor(Uint8 r, Uint8 g, Uint8 b) {
color[0] = r;
color[1] = g;
color[2] = b;
}
int* Rectangle::getColor()
{
return color;
}
// Method to draw the rectangle
void Rectangle::draw(SDL_Renderer* renderer) const {
if (renderer == nullptr) {
printf("Renderer is null, cannot draw rectangle.\n");
return;
}
// SDL_SetRenderDrawColor(renderer, 0xf0, 0xf0, 0xf0, 0xff); // Full opacity
// Define an SDL_Rect structure with the rectangle's properties
SDL_Rect fillRect = { x, y, w, h };
SDL_SetRenderDrawColor( renderer, color[0], color[1], color[2], 0xFF );
SDL_RenderFillRect( renderer, &fillRect );
}
my files structure:
SDL git:(main) ✗ ls
Makefile SDL2_image.framework loaded.png main.o ui
Readme.md a.txt main mysql your_program
SDL2.framework deb main.cpp steps.txt
➜ SDL git:(main) ✗
The problem arises when I do not use:
this->shapesObjectContainer.push_back(std::move(shape));
Because if I comment the if statement and then allow all the duplicate to be in the vector then the renderer work finer but I if comment the push line the renderer does not show anything.
main.cpp:
#include <SDL2/SDL.h>
#include <stdio.h>
#include "ui/buttons.hpp"
#include "ui/rectangle.hpp"
#include "ui/objects.hpp"
#include <string>
//Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
//Starts up SDL and creates window
bool init();
//Loads media
//Frees media and shuts down SDL
void close();
void showModal(SDL_Window* parent) {
SDL_Window* modal = SDL_CreateWindow("What is on your Todo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 300, 200, SDL_WINDOW_SHOWN);
// Render dialog contents here (e.g., buttons, text, etc.)
bool modalRunning = true;
SDL_Event e;
while (modalRunning) {
while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT) {
modalRunning = false;
}
// Handle other events (e.g., button clicks)
if (e.key.keysym.sym == SDLK_ESCAPE) {
// Close modal on click for demonstration
modalRunning = false;
}
}
// You would render your dialog content here
}
SDL_DestroyWindow(modal);
}
//Loads individual image as texture
SDL_Texture* loadTexture( std::string path );
//The window we'll be rendering to
SDL_Window* gWindow = NULL;
//The window renderer
SDL_Renderer* gRenderer = NULL;
//Current displayed texture
SDL_Texture* gTexture = NULL;
bool init()
{
//Initialization flag
bool success = true;
//Initialize SDL
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Set texture filtering to linear
if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
{
printf( "Warning: Linear texture filtering not enabled!" );
}
//Create window
gWindow = SDL_CreateWindow( "Timeit", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
if( gWindow == NULL )
{
printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Create renderer for window
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_SOFTWARE);
if( gRenderer == NULL )
{
printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Initialize renderer color
SDL_SetRenderDrawColor( gRenderer, 0x0F, 0x0F, 0x0F, 0xFF );
}
}
}
return success;
}
void close()
{
//Free loaded image
SDL_DestroyTexture( gTexture );
gTexture = NULL;
//Destroy window
SDL_DestroyRenderer( gRenderer );
SDL_DestroyWindow( gWindow );
gWindow = NULL;
gRenderer = NULL;
//Quit SDL subsystem
SDL_Quit();
}
SDL_Texture* loadTexture( std::string path )
{
//The final texture
SDL_Texture* newTexture = NULL;
//Load image at specified path
return newTexture;
}
int main( int argc, char* args[] )
{
//Start up SDL and create window
if( !init() )
{
printf( "Failed to initialize!\n" );
}
else
{
//Main loop flag
bool quit = false;
Objects objects(gRenderer);
//Event handler
SDL_Event e;
int coords[4] = {SCREEN_WIDTH/2 - 25, SCREEN_HEIGHT - 160,50, 20};
int coords2[4] = {SCREEN_WIDTH/2 - 25, SCREEN_HEIGHT - 260,50, 20};
int buttonColor[3] = {0xc2, 0xbf, 0xaf};
objects.addShapeObject(std::make_unique<Button>(coords, buttonColor, gRenderer,"Timer Button", "Start"));
objects.addShapeObject(std::make_unique<Button>(coords2, buttonColor, gRenderer,"Timer Button", "Start"));
// printf("UID :%i\n",testButton->UniqueIdentifier);
// seed48()
Uint32 startTime = SDL_GetTicks(); // Time at start of the loop
int frameCount = 0; // Number of frames rendered
float fps = 0.0f; // FPS value
//While application is running
while( !quit )
{
//Handle events on queue
SDL_Delay(2);
while( SDL_PollEvent( &e ) != 0 )
{
//User requests quit
// testButton->clickHandler(e,[&testButton](){
// if(testButton->getText() == "Stop")
// {
// testButton->setText("Start");
// }
// else
// {
// testButton->setText("Stop");
// }
// });
if( e.type == SDL_QUIT )
{
quit = true;
}
if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_m) {
showModal(gWindow);
}
}
//Clear screen
SDL_SetRenderDrawColor(gRenderer, 0x0F, 0x0F, 0x0F, 0xFF); // Set the clear color
SDL_RenderClear(gRenderer); // Clear the screen with the clear color
// Your drawing code
SDL_RenderCopy(gRenderer, gTexture, NULL, NULL); // Draw the texture
// Set a different color for points
SDL_SetRenderDrawColor(gRenderer, 0x7F, 0x2F, 0xFF, 0xFF);
for (int i = 0; i < SCREEN_HEIGHT; i += 4) {
SDL_RenderDrawPoint(gRenderer, SCREEN_WIDTH / 2, i);
}
for (int i = 0; i < SCREEN_WIDTH; i += 4) {
SDL_RenderDrawPoint(gRenderer, i, SCREEN_HEIGHT / 2);
}
objects.updateObjects(gRenderer); // Render your objects
SDL_RenderPresent(gRenderer); // Present the back buffer
printf("%s\n", SDL_GetError());
if(gRenderer == nullptr)
{
printf("gRenderer is no more");
}
frameCount++;
// Calculate FPS every second
Uint32 currentTime = SDL_GetTicks();
if (currentTime - startTime >= 1000) {
// Calculate FPS
fps = frameCount / ((currentTime - startTime) / 1000.0f);
// Reset timer and frame count for next second
startTime = currentTime;
frameCount = 0;
// Output FPS to console (you can render this to the screen if needed)
printf("FPS: %.2f\n", fps);
}
}
}
//Free resources and close SDL
close();
return 0;
}
The console shows Parameter 'renderer' is invalid