I am currently working on developing an in-house GUI for my own personal video game project, and I am currently done with creating the basic framework. It consists of these classes:
- GUI_Engine
- GUI_Element
- GUI_Panel
- GUI_Window
GUI_Element is an abstract base class for all my GUI classes I will create using my framework, and its child is GUI_Panel. GUI_Panel's child is GUI_Window.
GUI_Engine stores pointers to GUI_Element's and loads various PNG files to be used by all the GUI_Element's. These PNG files are stored in a SpritePool, a simple double vector that stores both SDL_Texture's as well as std::string's. The strings are the PNG file names.
I currently have two PNG files that are not rendering properly with the following code from the void GUI_Window::render() function, "window_top" and "window_bottom". These 16x16 files are tiles to render the top and bottom borders of the window; yet, they are the only two textures that are not rendering properly.
Here's the code for GUI_Element:
/*
Class: GUI_Element
Purpose: -To provide an ABSTRACT base class for all the GUI elements within the framework.
Notes: >The class already holds basic functions for hot, cold, select, and deslect.
Function pointers are going to use to provide callback/like functionality.
*/
#ifndef GUI_ELEMENT_H
#define GUI_ELEMENT_H
#include "globalvar.h"
#include "Input.h"
#include "SpritePool.h"
class GUI_Element
{
public:
GUI_Element() {}
GUI_Element(SpritePool gui_skin, int x, int y, int w, int h, bool visible, GUI_Element* parent);
virtual ~GUI_Element();
//set functions
void set_texture_ptr(SDL_Texture** texture) {texture_ptr = texture;}
void set_parent_element(GUI_Element* parent);
void set_frame(int x, int y, int w, int h);
void set_offset_x(int i) {offset_x = i;}
void set_offset_y(int i) {offset_y = i;}
void set_x(int i) {frame.x = i;}
void set_y(int i) {frame.y = i;}
void set_w(int i) {frame.w = i;}
void set_h(int i) {frame.h = i;}
void set_visible(bool b);
void set_hot(bool b);
void set_selected(bool b);
//get functions
SDL_Texture** get_texture_ptr() {return texture_ptr;}
GUI_Element* get_parent_element() {return parent_element;}
SDL_Rect* get_frame() {return &frame;}
int get_offset_x() {return offset_x;}
int get_offset_y() {return offset_y;}
int get_x() {return frame.x;}
int get_y() {return frame.y;}
int get_w() {return frame.w;}
int get_h() {return frame.h;}
//is_property functions
bool is_visible() {return visible;}
bool is_hot() {return hot;}
bool is_selected() {return selected;}
//on_action functions
virtual void on_hot() = 0;
virtual void on_cold() = 0;
virtual void on_select() = 0;
virtual void on_deselect() = 0;
//render/update/check input functions
virtual void render() = 0;
virtual void update() = 0;
virtual void check(Input &input) = 0;
protected:
int offset_x, offset_y;
SDL_Texture** texture_ptr;
GUI_Element* parent_element;
SDL_Rect frame;
bool visible;
bool hot;
bool selected;
void (*func_hot) ();
void (*func_select) ();
void (*func_cold) ();
void (*func_deselect) ();
private:
};
and here's the code for GUI_Window:
#ifndef GUI_WINDOW_H
#define GUI_WINDOW_H
#include "GUI_Panel.h"
class GUI_Window : public GUI_Panel
{
public:
GUI_Window(SpritePool gui_skin, int x, int y, int w, int h, bool visible, std::string str);
virtual ~GUI_Window();
void set_name(std::string str) {name = str;}
void set_menubar_properties(bool minimize, bool pin, bool exit);
void set_border_visible(bool b);
void set_bg_visible(bool b);
std::string get_name() {return name;}
bool get_minimize_visible() {return minimize_visible;}
bool get_pin_visible() {return pin_visible;}
bool get_exit_visible() {return exit_visible;}
bool get_border_visible() {return border_visible;}
bool get_bg_visible() {return bg_visible;}
virtual void on_hot();
virtual void on_cold();
virtual void on_select();
virtual void on_deselect();
//render/update/check input functions
virtual void render();
virtual void update();
virtual void check(Input &input);
protected:
std::string name;
bool minimize_visible;
bool pin_visible;
bool exit_visible;
bool border_visible;
bool bg_visible;
SDL_Rect tile_frame;
SDL_Texture** window_top;
SDL_Texture** window_bottom;
SDL_Texture** window_left;
SDL_Texture** window_right;
SDL_Texture** window_topleft;
SDL_Texture** window_topright;
SDL_Texture** window_bottomleft;
SDL_Texture** window_bottomright;
SDL_Texture** window_minimize;
SDL_Texture** window_pin;
SDL_Texture** window_exit;
SDL_Texture** gui_bg;
private:
};
#endif // GUI_WINDOW_H
and here's the code for GUI_Window::render():
void GUI_Window::render()
{
if (visible)
{
if (bg_visible)
{
SDL_RenderCopy(g_renderer, *gui_bg, NULL, &frame);
}
if (border_visible)
{
tile_frame.x = frame.x;
tile_frame.y = frame.y;
tile_frame.w = 16;
tile_frame.h = 16;
SDL_RenderCopy(g_renderer, *window_topleft, NULL, &tile_frame);
tile_frame.y = frame.y;
while (tile_frame.x < (frame.x + frame.w - 32))
{
SDL_RenderCopy(g_renderer, *window_top, NULL, &tile_frame);
tile_frame.x += 16;
}
tile_frame.x += 16;
SDL_RenderCopy(g_renderer, *window_topright, NULL, &tile_frame);
if (window_top == NULL)
std::cout << "oh well" << std::endl;
while (tile_frame.y < (frame.y + frame.h - 16))
{
tile_frame.y += 16;
SDL_RenderCopy(g_renderer, *window_right, NULL, &tile_frame);
}
SDL_RenderCopy(g_renderer, *window_bottomright, NULL, &tile_frame);
while (tile_frame.x > frame.x + 16)
{
tile_frame.x -= 16;
SDL_RenderCopy(g_renderer, *window_bottom, NULL, &tile_frame);
}
tile_frame.x -= 16;
SDL_RenderCopy(g_renderer, *window_bottomleft, NULL, &tile_frame);
while (tile_frame.y > frame.y + 16)
{
tile_frame.y -= 16;
SDL_RenderCopy(g_renderer, *window_left, NULL, &tile_frame);
}
}
if (exit_visible)
{
tile_frame.x = frame.x + frame.w - 16;
tile_frame.y = frame.y;
SDL_RenderCopy(g_renderer, *window_exit, NULL, &tile_frame);
}
if (pin_visible)
{
tile_frame.x = frame.x + frame.w - 32;
tile_frame.y = frame.y;
SDL_RenderCopy(g_renderer, *window_pin, NULL, &tile_frame);
}
if (minimize_visible)
{
tile_frame.x = frame.x + frame.w - 48;
tile_frame.y = frame.y;
SDL_RenderCopy(g_renderer, *window_minimize, NULL, &tile_frame);
}
}
}
I've checked the following:
- GUI_Engine loads all PNG files correctly. No SDL_Texture* are NULL;
- GUI_Window supposedly loads all the SDL_Texture* into SDL_Texture** correctly. None of these pointers are NULL.
- The loop logic in GUI_Window::render() runs correctly.
- Strangely, the top and bottom bars will render if the SDL_Texture** are changed to something other than window_top and window_bottom.
Note:
The SDL_Texture** names are the exact same as the PNG files.
Here's what it looks like:

Edit: Here's the texture loading code in my SpritePool class:
void SpritePool::load_texture(std::string path)
{
SDL_Texture* loaded_texture = NULL;
SDL_Surface* loaded_surface = NULL;
loaded_surface = IMG_Load(path.c_str());
if (loaded_surface == NULL)
std::cout << path << " unable to be loaded!" << std::endl;
else
{
loaded_texture = SDL_CreateTextureFromSurface(g_renderer, loaded_surface);
if (loaded_texture == NULL)
std::cout << path << " unable to be converted to SDL_Texture*!" << std::endl;
else
std::cout << path << " loaded!" << std::endl;
}
SDL_FreeSurface(loaded_surface);
texture_pool.push_back(loaded_texture);
texture_names.push_back(path);
}
Someone please help me!
set_texture_pointer: if you are getting the SDL_Texture** by doing something likeptr = &texture_pool[i]and then you keep adding other textures totexture_pool(which I assume is a vector), it's possible than when thetexture_poolresizes internally, that the pointers are moved around and your SDL_Texture** are no longer valid. - ysalmiSDL_Textureis the object and SDL is returning to you a pointer to the object. There is no need to store a pointer to this pointer. SDL stores the objects internally and gives you pointers to them. Perhaps check out a tutorial on c pointers: tutorialspoint.com/cprogramming/c_pointers.htm . They can occasionally be tricky. - ysalmi