-2
\$\begingroup\$

I'm trying to create a 2D game, and I'm trying to use a layered drawing model.

First I draw the background, then the world, then the constructs (buildings etc.), then the objects (NPC's etc.) and lastly a debug overlay.

For this debug overlay I'm drawing the bounding boxes of all textures on screen, as well as some text, but for some reason, the text isn't visible.

I've tried and checked everything I can think of (Checked SDL_GetError(), TTF_GetError(), IMG_GetError(), Check if I was using the right color, checked TTF_WasInit(), ...) but I simply can not figure out what is going wrong.

I'll paste some of the relevant code below, if there's anything else you'd like to see, I'll add it in an edit.

Texture.hpp:

#ifndef GAME_TEXTURE_HPP
#define GAME_TEXTURE_HPP

#include <iomanip>
#include <iostream>
#include <string>

#include <SDL_system.h>
#include <SDL_image.h>
#include <SDL_ttf.h>

#include "../Math/Coord.hpp"
#include "../Math/Rect.hpp"
#include "../Settings/Object.hpp"

namespace Rendering
{
    class Texture : public Stringable
    {
        private:
            bool empty;

            double width;
            double height;

            SDL_Texture* sdlTexture;
            SDL_Renderer* sdlRenderer;

            void freeTexture();

        public:
            Texture();

            Texture (SDL_Renderer* renderer);

            void loadFromFile (std::string path);

            void loadFromText (std::string txt, SDL_Color color, TTF_Font* font);

            Math::Rect render (const Math::Coord& c) const;

            bool isEmpty () const;

            double getHeight() const;

            double getWidth() const;

            ~Texture();

            std::string toString () const;
    };
}

#endif //GAME_TEXTURE_HPP

A few extracts from Texture.cpp

void Texture::loadFromFile(std::string path)
    {
        this->freeTexture();

        SDL_Texture* newTexture = NULL;

        SDL_Surface* surface = IMG_Load(path.c_str());

        if (surface != NULL)
        {
            SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGB (surface->format, 0, 0xFF, 0xFF));

            newTexture = SDL_CreateTextureFromSurface(this->sdlRenderer, surface);

            if (newTexture != NULL)
            {
                this->width = surface->w;
                this->height = surface->h;
                this->empty = false;
            }
            else
            {
                std::cout << "[Texture::loadFromFile]\tCould not create texture from image" << std::endl;
            }

            SDL_FreeSurface(surface);
        }
        else
        {
            std::cout << "[Texture::loadFromFile]\tUnable to load surface from file" << std::endl;
        }

        this->sdlTexture = newTexture;
        SDL_SetTextureBlendMode(this->sdlTexture, SDL_BLENDMODE_NONE);
    }

    void Texture::loadFromText(std::string txt, SDL_Color color, TTF_Font* font)
    {
        this->freeTexture();

        SDL_Texture* newTexture;

        SDL_Surface* textSurface = TTF_RenderText_Solid(font, txt.c_str(), color);

        if (textSurface != NULL)
        {
            newTexture = SDL_CreateTextureFromSurface(this->sdlRenderer, textSurface);

            if (newTexture != NULL)
            {
                this->width = textSurface->w;
                this->height = textSurface->h;
                this->empty = false;
            }
            else
            {
                std::cout << "[Texture::loadFromText]\tAn Error occurred while creating a texture from a surface: " << TTF_GetError() << std::endl;
            }
        }
        else
        {
            std::cout << "[Texture::loadFromText]\tAn Error occurred while creating a surface from a text: " << TTF_GetError() << std::endl;
        }

        this->sdlTexture = newTexture;
        SDL_SetTextureBlendMode(this->sdlTexture, SDL_BLENDMODE_NONE);
    }

    Math::Rect Texture::render(const Math::Coord &c) const
    {
        if (!this->empty)
        {
            SDL_Rect rect {(int) c.getX(), (int) c.getY(), (int) this->width, (int) this->height};
            SDL_RenderCopy(this->sdlRenderer, this->sdlTexture, NULL, &rect);
            std::cout << "Rendering: " << this->toString() << std::endl;
            return Math::Rect(c.getX(), c.getY(), this->width, this->height);
        }
    }

Main render loop:

void GameScreen::renderLoop()
    {
        bool quit = false;
        SDL_Event e;

        Rendering::Texture backgroundTexture = Rendering::Texture (this->sdlRenderer);

        backgroundTexture.loadFromFile(Settings::Object::getInstance().get(Settings::GENERAL_SECTION_KEY).get(Settings::GENERAL_BACKGROUND_KEY));

        this->add(backgroundTexture, BACKGROUND);

        while(!quit)
        {
            while(SDL_PollEvent(&e) != 0)
            {
                switch(e.type)
                {
                    case SDL_QUIT:
                        quit = true;
                        break;

                    case SDL_KEYDOWN:
                        switch (e.key.keysym.sym)
                        {
                            case SDLK_BACKSPACE:
                                this->debugMask = !this->debugMask;
                                std::cout << "Toggled debug mask" << std::endl;
                                break;

                            default:
                                std::cout << "Unknown key pressed" << std::endl;
                                break;
                        }

                    default:
                        std::cout << "Unknown event occured" << std::endl;
                        break;
                }
            }

            SDL_SetRenderDrawColor(this->sdlRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
            SDL_RenderClear(this->sdlRenderer);

            std::vector <Math::Rect> boundingBoxes = std::vector <Math::Rect> ();

            for (size_t i = 0; i < this->backgroundQueue.size(); i++)
            {
                boundingBoxes.push_back(this->backgroundQueue.at(i).render(Math::Coord (0,0)));
            }

            for (size_t i = 0; i < this->worldQueue.size(); i++)
            {
                boundingBoxes.push_back(this->worldQueue.at(i).render(Math::Coord (0,0)));
            }

            for (size_t i = 0; i < this->constructQueue.size(); i++)
            {
                boundingBoxes.push_back(this->constructQueue.at(i).render(Math::Coord (0,0)));
            }

            for (size_t i = 0; i < this->objectQueue.size(); i++)
            {
                boundingBoxes.push_back(this->objectQueue.at(i).render(Math::Coord(0, 0)));
            }

            if (this->debugMask)
            {
                Rendering::Texture debugTexture = this->generateDebugOverlay();

                if (!debugTexture.isEmpty())
                {
                    boundingBoxes.push_back(debugTexture.render(Math::Coord(0, 0)));
                }

                Settings::Section section = Settings::Object::getInstance().get(Settings::DEBUG_SECTION_KEY);

                SDL_Color boundingBoxColor = Rendering::colorFromString(section.get(Settings::DEBUG_BOUNDING_BOX_COLOR_KEY));

                SDL_SetRenderDrawColor(this->sdlRenderer, boundingBoxColor.r, boundingBoxColor.g, boundingBoxColor.b, boundingBoxColor.a);

                for (Math::Rect box : boundingBoxes)
                {
                    SDL_Rect rect = box.toSDLRect();
                    SDL_RenderDrawRect(this->sdlRenderer, &rect);
                }
            }

            SDL_RenderPresent(this->sdlRenderer);
        }


        this->quit();
    }

Generating Debug output (Really primitive right now, since well, it's not even working)

Rendering::Texture GameScreen::generateDebugOverlay() const
    {
        Settings::Section section = Settings::Object::getInstance().get(Settings::DEBUG_SECTION_KEY);
        std::string fontStr = section.get(Settings::DEBUG_FONT_KEY);
        std::string fontSizeStr = section.get(Settings::DEBUG_FONT_SIZE_KEY);
        std::string fontColorStr = section.get(Settings::DEBUG_FONT_COLOR_KEY);

        int fontSize = stoi(fontSizeStr);

        TTF_Font* font = TTF_OpenFont(fontStr.c_str(), fontSize);

        if (font != NULL)
        {
            SDL_Color debugColor = Rendering::colorFromString(fontColorStr);

            std::string debugText = "DEBUG\nFrame Cap: " + std::to_string(this->frameCap) + "FPS";

            Rendering::Texture debugTexture = Rendering::Texture(this->sdlRenderer);

            debugTexture.loadFromText(debugText, debugColor, font);

            return debugTexture;
        }
        else
        {
            std::cout << "[GameScreen::renderloop()]\tAn error occurred while trying to create a TTF font: " << TTF_GetError() << std::endl;
            return Rendering::Texture();
        }
    }

The Settings stuff is loaded when the program starts, the game reads all of its settings from an INI file (And this all appears to be working)

When running the main render loop the program reports that it's rendering the following textures:

Rendering: Texture: Empty: 0 1920x1080
Rendering: Texture: Empty: 0 176x18

I'm assuming the 1920x1080 is my background (which is a 1920x1080 PNG image) and the 176x18 is the debug overlay (this has a font size of 16, so 18px tall is realistic) The empty flag on a texture is used to indicate if it's been used already or not, before anything is loaded onto it, the flag is set to 1

EDIT: Noticed for Texture::loadFromFile() I'm using SDL_MapRGB(format, 0,0xFF,0xFF) changed the 0 for red to a 0xFF, this did not solve anything unfortunately...

EDIT2: I tried to run the same code, but with a different font, the reported size changed, which seems like an indicator that something is being rendered, but its not visible Here's 2 screenshots to give a better idea of what's wrong: http://imgur.com/a/NLzme

EDIT3: I just noticed I'm not undef-ing main, but undef-ing it now does not make any difference. As requested, the code for colorFromString:

SDL_Color colorFromString (std::string colorString)
    {
        if ((colorString.at(0) == '#') && (colorString.size() == 9))
        {
            std::string redString = colorString.substr(1,2);
            std::string greenString = colorString.substr(3,2);
            std::string blueString = colorString.substr(5,2);
            std::string alphaString = colorString.substr(7,2);

            uint8_t r = (uint8_t) (stoi(redString, nullptr, 16) & 0x000000FF);
            uint8_t g = (uint8_t) (stoi(greenString, nullptr, 16) & 0x000000FF);
            uint8_t b = (uint8_t) (stoi(blueString, nullptr, 16) & 0x000000FF);
            uint8_t a = (uint8_t) (stoi(alphaString, nullptr, 16) & 0x000000FF);

            return SDL_Color {r, g, b, a};
        }
        else
        {
            std::cout << "[colorFromString()]\tInvalid color string passed to function: " + colorString << std::endl;
        }
    }

colorFromString is used to parse an HTML-like color string into an SDL color (Right now the config has #FF0000FF (RGBA) as the color for the bounding boxes and the debug text)

\$\endgroup\$
  • \$\begingroup\$ I copied the relevant parts of your code into a very simple program of mine and everything works just fine. Could the text color be an issue? Have you tried with SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); for the text? For reference, this is the code I used: pastebin.com/stbh7aN5. So either I left out some important section of your code that causes the issue or there's something else quite weird going on. \$\endgroup\$ – Tyyppi_77 Aug 6 '17 at 15:38
  • \$\begingroup\$ I've tried using SDL_BLENDMODE_BLEND, the results can be seen here: imgur.com/a/wRtgQ, (Oops, misread your reply, The screenshots shown use blend mode for both image and text, setting blend mode for just text yields the same results as in the original post) \$\endgroup\$ – shmoo6000 Aug 6 '17 at 15:45
  • \$\begingroup\$ Could you try run the example I posted (replace my font path with yours of course)? \$\endgroup\$ – Tyyppi_77 Aug 6 '17 at 15:52
  • \$\begingroup\$ Running your code gives me a black window with red text ("TEST TEXT") (seems to be working like it should) \$\endgroup\$ – shmoo6000 Aug 6 '17 at 15:56
  • 1
    \$\begingroup\$ HUZZAH! I just made some progress! By placing the Texture::render() method for the debug text in the generateDebugMethod() I was able to get output. I think this happens because I haven't overridden operator= for my Texture class \$\endgroup\$ – shmoo6000 Aug 6 '17 at 17:12
-1
\$\begingroup\$

Solution: To solve this issue you need to implement the assignment operator. There is no way to copy an SDL_Texture (or at least not one that I know of) so you'll have to pass the pointer. This also means that you can not have the destructor get rid of the texture, because when the object goes out of scope (in the generateDebugOverlay() method), the texture will be deleted. Here's my implementation:

Texture& Texture::operator= (const Texture& texture)
    {
        if (this != &texture)
        {
            *this = Texture();

            this->empty = texture.empty;
            this->width = texture.width;
            this->height = texture.height;

            this->sdlTexture = texture.sdlTexture;
            this->sdlRenderer = texture.sdlRenderer;
        }
        else
        {
            std::cerr << "[WARNING]\tTried to assign Texture to itself!" << std::endl;
        }
    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.