Monday, May 20, 2024
HomeGame Developmentc++ - How can I properly deallocate my assets and prevent memory...

c++ – How can I properly deallocate my assets and prevent memory leaks? SDL2


First I declare some global variables

TTF_Font* Pusab;
TTF_Font* HPusab;
TTF_Font* SPusab;
SDL_Surface* message;
SDL_Texture* slotOne;
SDL_Texture* slotTwo;
SDL_Texture* slotThree;
SDL_Texture* slotFour;
SDL_Texture* slotFive;
SDL_Texture* slotSix;
SDL_Texture* slotFPS;
SDL_Texture* slotfpsNum;
SDL_Texture* slotNine;
SDL_Rect rslotOne;
SDL_Rect rslotTwo;
SDL_Rect rslotThree;
SDL_Rect rslotFour;
SDL_Rect rslotFive;
SDL_Rect rslotSix;
SDL_Rect rslotFPS;
SDL_Rect rslotfpsNum;
SDL_Rect rslotNine;

I’ve created a class that makes text objects called RText

class Rtext {
private:
    // Object attributes
    SDL_Texture* slot; // Texture that holds the final image
    SDL_Surface* message; // The surface created by the message string passed through
    SDL_Rect slotRect; // Final Rectangle data
    TTF_Font* font; // The font of the text
    const char* messtring; // The string passed through
    SDL_Color color; // The color of the text
    int x; // The x position of the text
    int y; // The y position of the text

    //Creates the text using the parameters set by setText
    bool create(bool wrapped, int width) {
        if (wrapped == true) {
            message = TTF_RenderText_Blended_Wrapped(font, messtring, color, width); // Create a wrapped text surface

            if (message == NULL) {
                std::cout << "Could not create message! " << SDL_GetError() << std::endl;
                return false;
                // code will stop running here because I used a return statement
            }

            slot = SDL_CreateTextureFromSurface(gRenderer, message); // Create a texture from the message surface

            if (slot == NULL) {
                std::cout << "Could not convert message! " << SDL_GetError() << std::endl;
                return false;
                // code will stop running here because I used a return statement
            }

            slotRect.x = x;  //controls the rect's x coordinate 
            slotRect.y = y; // controls the rect's y coordinte
            slotRect.w = message->w; // controls the width of the rect
            slotRect.h = message->h; // controls the height of the rect

            return true;
        }
        else {
            message = TTF_RenderText_Blended(font, messtring, color); // Create a text surface

            if (message == NULL) {
                std::cout << "Could not create message! " << SDL_GetError() << std::endl;
                return false;
                // code will stop running here because I used a return statement
            }

            slot = SDL_CreateTextureFromSurface(gRenderer, message); // Create a texture from the message surface

            if (slot == NULL) {
                std::cout << "Could not convert message! " << SDL_GetError() << std::endl;
                return false;
                // code will stop running here because I used a return statement
            }

            slotRect.x = x;  //controls the rect's x coordinate 
            slotRect.y = y; // controls the rect's y coordinte
            slotRect.w = message->w; // controls the width of the rect
            slotRect.h = message->h; // controls the height of the rect

            return true;
        }
    }

public:
    //Calls the create method and returns the finished texture
    SDL_Texture* getSlot(bool wrapped, int width) {
        if (create(wrapped, width)) {
            return slot;
        }
    }

    //Calls the create method and returns the finished texture's rectangle data
    SDL_Rect getRect(bool wrapped, int width) {
        if (create(wrapped, width)) {
            return slotRect;
        }
    }

    //Pulls in the text and sets all the text parameters
    void setText(TTF_Font* f, const char* m, SDL_Color c, int _x, int _y) {
        font = f;
        messtring = m;
        color = c;
        x = _x;
        y = _y;
    }
};

Then I’ve made some global objects

// Create Global Text Objects
Rtext tobj1;
Rtext tobj2;
Rtext tobj3;
Rtext tobj4;
Rtext tobj5;
Rtext tobj6;
Rtext FPS;
Rtext fpsNum;
Rtext tobj9;

I have a function that loads in the assets to some global variables. Here’s a snippet of the function that shows 1 object being loaded

bool loadMenuStart() {
    // Load all the text one by one
    tobj1.setText(HPusab, "Nightmare at Walmart", White, 50, 50);
    slotOne = tobj1.getSlot(false, NULL);

    if (slotOne == NULL) {
        std::cout << "Could not create Title! " << SDL_GetError() << std::endl;
        return false;
        // code will stop running here because I used a return statement
    }

    rslotOne = tobj1.getRect(false, NULL);
}

I have a similar function that loads another scene using the same object tobj1 and same global variables slotOne and rslotOne

My problem is that when I load the other scene, the old scene’s data isn’t being deallocated. I know the code I’ve shown so far doesn’t deallocate the resources, but here’s what I’ve tried so far with deallocation.

The first thing I’ve tried is adding a function inside my RText class called free() that looks like this

free() {
    SDL_DestroyTexture(slot);
    SDL_Freesurface(message);
}

Then I call this function at the beginning of my function that loads the next scene so it looks like this

bool loadNewGame() {
    tobj1.free();
    
    // Load all the text one by one
    tobj1.setText(Pusab, "I said I would never go back to Walmart,", White, 50, 50);
    slotOne = tobj1.getSlot(true, 1198);

    if (slotOne == NULL) {
        std::cout << "Could not create Title! " << SDL_GetError() << std::endl;
        return false;
        // code will stop running here because I used a return statement
    }

    rslotOne = tobj1.getRect(true, 1198);

However, this didn’t seem to work and loading the new scene caused a memory leak. The next thing I tried was destroying the surface and textures in the create() function instead like this

bool create(bool wrapped, int width) {
        if (wrapped == true) {
            message = TTF_RenderText_Blended_Wrapped(font, messtring, color, width); // Create a wrapped text surface

            if (message == NULL) {
                std::cout << "Could not create message! " << SDL_GetError() << std::endl;
                return false;
                // code will stop running here because I used a return statement
            }

            slot = SDL_CreateTextureFromSurface(gRenderer, message); // Create a texture from the message surface

            if (slot == NULL) {
                std::cout << "Could not convert message! " << SDL_GetError() << std::endl;
                return false;
                // code will stop running here because I used a return statement
            }

            slotRect.x = x;  //controls the rect's x coordinate 
            slotRect.y = y; // controls the rect's y coordinte
            slotRect.w = message->w; // controls the width of the rect
            slotRect.h = message->h; // controls the height of the rect

            SDL_DestroyTexture(slot);
            SDL_Freesurface(message);

            return true;
        }
        else {
            message = TTF_RenderText_Blended(font, messtring, color); // Create a text surface

            if (message == NULL) {
                std::cout << "Could not create message! " << SDL_GetError() << std::endl;
                return false;
                // code will stop running here because I used a return statement
            }

            slot = SDL_CreateTextureFromSurface(gRenderer, message); // Create a texture from the message surface

            if (slot == NULL) {
                std::cout << "Could not convert message! " << SDL_GetError() << std::endl;
                return false;
                // code will stop running here because I used a return statement
            }

            slotRect.x = x;  //controls the rect's x coordinate 
            slotRect.y = y; // controls the rect's y coordinte
            slotRect.w = message->w; // controls the width of the rect
            slotRect.h = message->h; // controls the height of the rect

            SDL_DestroyTexture(slot);
            SDL_Freesurface(message);

            return true;
        }
    }

However this caused the rendering to stop working all together and I would just have a black screen on my window. My question is how can a properly deallocate the resources when I do a scene change? Is there a better way I can implement my text system?

Here’s the rendering function if you need it

void draw() {
    if (gameState == "menu") {
        SDL_RenderCopy(gRenderer, slotOne, NULL, &rslotOne);
        SDL_RenderCopy(gRenderer, slotTwo, NULL, &rslotTwo);
        SDL_RenderCopy(gRenderer, slotThree, NULL, &rslotThree);
        SDL_RenderCopy(gRenderer, slotFour, NULL, &rslotFour);
        SDL_RenderCopy(gRenderer, slotFive, NULL, &rslotFive);
        SDL_RenderCopy(gRenderer, slotSix, NULL, &rslotSix);
        SDL_RenderCopy(gRenderer, slotFPS, NULL, &rslotFPS);
        SDL_RenderCopy(gRenderer, slotfpsNum, NULL, &rslotfpsNum);

        SDL_RenderPresent(gRenderer);

        SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 255);
        SDL_RenderClear(gRenderer);
    }

    else if (gameState == "newgame") {
        SDL_RenderCopy(gRenderer, slotOne, NULL, &rslotOne);
        SDL_RenderCopy(gRenderer, slotTwo, NULL, &rslotTwo);
        SDL_RenderCopy(gRenderer, slotThree, NULL, &rslotThree);
        SDL_RenderCopy(gRenderer, slotFour, NULL, &rslotFour);
        SDL_RenderCopy(gRenderer, slotFive, NULL, &rslotFive);
        SDL_RenderCopy(gRenderer, slotSix, NULL, &rslotSix);
        SDL_RenderCopy(gRenderer, slotFPS, NULL, &rslotFPS);
        SDL_RenderCopy(gRenderer, slotfpsNum, NULL, &rslotfpsNum);
        SDL_RenderCopy(gRenderer, slotNine, NULL, &rslotNine);

        SDL_RenderPresent(gRenderer);

        SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 255);
        SDL_RenderClear(gRenderer);
    }

    else if (gameState == "ingame") {
        SDL_RenderCopy(gRenderer, slotFPS, NULL, &rslotFPS);
        SDL_RenderCopy(gRenderer, slotfpsNum, NULL, &rslotfpsNum);
        
        SDL_RenderPresent(gRenderer);

        SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 255);
        SDL_RenderClear(gRenderer);
    }
}

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments