genfy
genfy

Reputation: 83

SDL_TTF slow printing effect

So, I'm working on a command prompt game, and right now I'm starting small. I've gotten the basics down for a slow printing text system, but I've encountered a problem that I'm unsure how to fix.

Basically; the text stretches to fit the predefined box: preview and by predefined box, I mean the SDL_Rect's I defined as srcrect and dstrect.

Now, a solution would be to simply extend the SDL_Rect's to fit my text, and as I'm using a Monospaced font, this would be fairly easy. But I'm just left thinking; "There must be a better way!".

Finally, here's my code if you need it:

#include<iostream>
#include<SDL.h>
#include<string>
#include<SDL_ttf.h>

std::string text = "";
std::string textToAdd = "Hello there, how are you doing?";
int pos = 0;

void handleEvents(SDL_Event e, bool* quit){
    while(SDL_PollEvent(&e) > 0){
        if(e.type == SDL_QUIT){
            *quit = true;
        }
    }
}

void render(SDL_Renderer* renderer, SDL_Texture* textToRender, SDL_Rect srcrect, SDL_Rect dstrect){
    SDL_RenderClear(renderer);

    SDL_RenderCopy(renderer, textToRender, &srcrect, &dstrect);

    SDL_RenderPresent(renderer);
}

Uint32 calcText(Uint32 interval, void *param){
    if(pos < textToAdd.length()){
        text = text + textToAdd[pos];
        pos++;
    }
    return interval;
}

int main( int argc, char *argv[] ) {
    SDL_Init(SDL_INIT_EVERYTHING);
    TTF_Init();
    SDL_Window* window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 600, 600, SDL_RENDERER_ACCELERATED);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, 0, 0);

    bool quit = false;
    SDL_Event e;

    SDL_TimerID repeatText = SDL_AddTimer(70, calcText, NULL);

    TTF_Font* font = TTF_OpenFont("Hack-Regular.ttf", 28);
    SDL_Color color = {255, 255, 255};
    SDL_Surface* textSurface;
    SDL_Texture* textTexture;

    SDL_Rect srcrect;
    SDL_Rect dstrect;

    srcrect.x = 0;
    srcrect.y = 0;
    srcrect.w = 580;
    srcrect.h = 32;
    dstrect.x = 10;
    dstrect.y = 10;
    dstrect.w = 580;
    dstrect.h = 32;

    while(!quit){
        handleEvents(e, &quit);
        render(renderer, textTexture, srcrect, dstrect);



        textSurface = TTF_RenderText_Solid(font, text.c_str(), color);
        textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
    }

    SDL_DestroyWindow(window);
    SDL_DestroyRenderer(renderer);

    window = NULL;
    renderer = NULL;
    TTF_Quit();
    SDL_Quit();
    return 0;
}

Upvotes: 2

Views: 509

Answers (1)

Lasoloz
Lasoloz

Reputation: 262

I made it using a little bit different methods. First of all, I suggest you to read this tutorial: http://lazyfoo.net/tutorials/SDL/index.php. Secondly: try to use initialization checks at critical points, like in this example:

//...
if (!myInitFunction())
{
    //error message and quit without crash
}
else
{
    //continue game
}
//...

Let's get back to your problem:

Yes it's possible, what you want, getting dstrect.w from surface->w. But not so simply. Because at first run of while loop we get into a crash if we do dstrect.w = surcface->h then your game crashes. Why? Because the string is equal with nothing: "".

To avoid that, you should simply start with a spaced string or put the 'H' character in the string before start. (Actually I made it in an another way without using SDL_TimerID but I don't want to complicate it.)

Also, you should follow the "order rule": INPUT >>> HANDLING >>> RENDER.

So your code should look like this:

//...
std::string text = "H";
std::string textToAdd = "ello there, how are you doing?";
//...
while(!quit)
{
    handleEvents(e, &quit);

    textSurface = TTF_RenderText_Solid(font, text.c_str(), color);
    dstrect.w = textSurface->w; //change the width of rectangle
    textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);

    render(renderer, textTexture, srcrect, dstrect);
}
//...

Another suggestion is that try to generalize this render method, and try to do it in console application if you haven't did it.

IMPORTANT EDIT: I made it to repeat text, and it slowed down. I realized that there is a memory leak! So after using or before recreating surface you must delete it with SDL_FreeSurface and delete texture by using SDL_DestroyTexture.

Edit 2: So, responding to your comment, here it is your while loop:

{
handleEvents(e, &quit);

if (textSurface != NULL)
{
    SDL_FreeSurface(textSurface);
    //textSurface = NULL; //optional
}
textSurface = TTF_RenderText_Solid(font, text.c_str(), color);
dstrect.w = textSurface->w; //change the width of rectangle

if (textTexture != NULL)
{
SDL_DestroyTexture(textTexture);
//textTexture = NULL;
}
textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);

render(renderer, textTexture, srcrect, dstrect);
}

Note: you must declare Surface and Texture pointers as NULLs so the if part can work.

Upvotes: 2

Related Questions