Beginner needs help (SDL_Mixer & SDL_ttf)

Started by
10 comments, last by Chad Smith 18 years, 1 month ago
Hi, This is my first post on gamedev.net so please be gentle with me :) Apologies for the length of this post, I've tried to be as informative on my problems as possible. I'm a beginner C++ programmer who's decided to dive in and get his feet wet with SDL after reading about half of Michael Dawson's excellent book, Beginning C++ Game Programming. I'm using Dev-C++ as my compiler (running on Windows XP), and as well as C++ & SDL I'm using SDL_mixer, SDL_ttf & SDL_gfx. I've found it pretty hard going so far but have managed to get a quick & dirty simple Pong game going. However, I've hit upon a couple of snags which I need a little help with: 1: SDL_mixer problems- I'm using SDL_mixer to provide my background music and for sound effects when the ball bounces off the walls & paddles. I've got it all working and have set the music to loop infinitely, but when the music finishes and returns to the start I get a little gap. The music I'm using was written to loop perfectly and does so when I play it outside of my program, so how can I get rid of this? I'm guessing it might have something to do with the chunksize? When I initialise SDL_mixer the code I'm using is Mix_OpenAudio( 44100, AUDIO_S16, 2, 2048 ); and the music is .ogg format if that makes any difference. The command to play the music (Mix_PlayMusic( music, -1 );) is outside my while(gameRunning) main game loop. I've also got a minor problem with panning- I've set the sounds which play when the ball bounces off the left/right paddles to pan to the corresponding left & right. It works, but occasionally the panning glitches and momentarily (literally for milliseconds) plays the sound in the centre. I have all four sound effects I'm using set to different channels. Are these problems symptomatic of SDL_mixer itself or is it something I'm doing wrong, which is fixable? Should I try using a different sound library instead? 2. SDL_ttf problem- how do I go about using SDL_ttf to render my score integers? I have my scores as integer variables which increment when the ball goes off the side of the screen. I can get SDL_ttf to draw to the screen when it's got a const char* passed to TTF_RenderText_Solid(), but trying to get the int in there produces the compile error "invalid conversion from `int' to `const char*'". I'd guess I'd have to convert the int somehow but if it needs to be const, how can it be changed when the score updates? Sorry, I don't think I'm explaining myself very well so please bear in mind that I'm a beginner and this has me totally stumped. If anyone can help with either of the above then I would be very appreciative. I can provide some more info/code if necessary. Cheers!
Advertisement
2) use sprintf to convert the score int to string

        //A temp string        char scoreString[ 64 ];                //Convert the score to a string        sprintf( scoreString, "%d", score );

Learn to make games with my SDL 2 Tutorials

Cheers for your prompt response, hadn't come across sprintf before so I'll look it up and do a bit of research.

However, since then I've come across a much more serious problem. The section of code which writes the word "pong" at the top of the screen (which will display the score when I work out how to do it) causes the amount of memory my game uses to continuously go up by 2MB per second, according to Windows Task Manager.

I know it's just this little section, as when commented out the bug goes away. I've got it towards the end of my while(gameRunning) loop, after the part where I use SDL_gfx to render the bat/ball etc and just before SDL_Flip(screen);

                    //draw word pong at top          score = TTF_RenderText_Blended (font, "PONG", textColor);                    SDL_Rect offset;              //Get offsets          offset.x = BOUNDARY_TOP_LEFT;          offset.y = BOUNDARY_TOP_LEFT /2;          SDL_BlitSurface( score, NULL, screen, &offset );                    SDL_Flip(screen);


How come it's using up so much memory, and what can I do?
Because you're render the message surface every frame, you're creating a new surface every frame and not getting rid of it.

Render the message surface once, then just use it over and over. Then free it when you're done.

Learn to make games with my SDL 2 Tutorials

You don't have to, but it is A LOT easier to make a function on your own to display text. A simple function I use looks like this:


void DrawText(SDL_Surface* screen, const char* theText, int size, int x, int y, int r, int g, int b){     //Font variable     TTF_Font *text = TTF_OpenFont("Font/KABELU.TTF", size);     if(text==NULL)     {          fprintf(stderr, "Could not load the font entitled KABELU");     }     //Color variable for the text     SDL_Color textColor = {r, g, b};     //create a surface, and name it message     SDL_Surface* message = TTF_RenderText_Solid(text, theText, textColor);     //font rect     SDL_Rect font = {x, y, 0, 0};     SDL_BlitSurface(message, NULL, screen, &font);     SDL_FreeSurface(message);     TTF_CloseFont(text);}



Hope that helps.

Chad.

Quote:Original post by Chad Smith
You don't have to, but it is A LOT easier to make a function on your own to display text. A simple function I use looks like this:


void DrawText(SDL_Surface* screen, const char* theText, int size, int x, int y, int r, int g, int b){     //Font variable     TTF_Font *text = TTF_OpenFont("Font/KABELU.TTF", size);     if(text==NULL)     {          fprintf(stderr, "Could not load the font entitled KABELU");     }     //Color variable for the text     SDL_Color textColor = {r, g, b};     //create a surface, and name it message     SDL_Surface* message = TTF_RenderText_Solid(text, theText, textColor);     //font rect     SDL_Rect font = {x, y, 0, 0};     SDL_BlitSurface(message, NULL, screen, &font);     SDL_FreeSurface(message);     TTF_CloseFont(text);}



Hope that helps.

Chad.


Oh God.....

You have any idea how much CPU you're wasting with that?

Learn to make games with my SDL 2 Tutorials

Quote:Original post by Chad Smith
You don't have to, but it is A LOT easier to make a function on your own to display text. A simple function I use looks like this:


void DrawText(SDL_Surface* screen, const char* theText, int size, int x, int y, int r, int g, int b){     //Font variable     TTF_Font *text = TTF_OpenFont("Font/KABELU.TTF", size);     if(text==NULL)     {          fprintf(stderr, "Could not load the font entitled KABELU");     }     //Color variable for the text     SDL_Color textColor = {r, g, b};     //create a surface, and name it message     SDL_Surface* message = TTF_RenderText_Solid(text, theText, textColor);     //font rect     SDL_Rect font = {x, y, 0, 0};     SDL_BlitSurface(message, NULL, screen, &font);     SDL_FreeSurface(message);     TTF_CloseFont(text);}



Hope that helps.

Chad.


Sir, please don't.... With that every frame you are:

    1. Loading a font
    2. Loading a surface FROM that font
    3. Blitting the surface
    4. Freeing the surface
    5. Freeing the font

This would lower the framerate plus waste CPU... But then again, if you have 1GB/2GB of ram and a blazing fast computer and you really wouldn't care-less, then go for it....

Don't take this the wrong way... I'm just trying to say that this isn't really an efficient way... I'm sorry if it sounds harsh or anything.
That is a good idea, but you load the font from the disk each time you call it. That is extremely inefficient.

Here is a more efficient solution.
class FontWriter{private:     TTF_Font* font;public:     FontWriter(const std::string& filename, int size)     {          font = TTF_OpenFont(filename.c_str(), size);          // You should hanle errors here     }     ~FontWriter( )     {         if(font)         {             TTF_CloseFont(font);             font = NULL;         }     }     void Write(const std::string& text, int x, int y, int r, int g, int b)     {         SDL_Color text_color = {r, g, b};         SDL_Surface* message = TTF_RenderText_Solid(font, text.c_str(), textColor);         SDL_Rect dest = {x, y, 0, 0};         SDL_BlitSurface(message, NULL, SDL_GetVideoSurface(), &dest);         SDL_FreeSurface(message);     }};


Then you can use it like so:
FontWriter arial12("Fonts/Arial.ttf", 12);arial12.Write("Hello", 0, 0, 255, 0, 0);
Quote:Original post by Simian Man
That is a good idea, but you load the font from the disk each time you call it. That is extremely inefficient.

Here is a more efficient solution.
*** Source Snippet Removed ***

Then you can use it like so:
FontWriter arial12("Fonts/Arial.ttf", 12);arial12.Write("Hello", 0, 0, 255, 0, 0);


That's great and all, but there is even a better way:
class FontWriter {    SDL_Surface *pText;    TTF_Font *pFont;    void Blit(...);    void ChangeText(char const*const text);};

In change text you would assign the surface a new one with new text... And the blit just blits it to a position... This is a better solution because for example, if you have a huge menu that just says: "Press SPACE to continue...", and doesn't change, then you will NOT want a new surface being created.
You are right. The way I use SDL_ttf is to render all of the letters to a surface once and then get textures for individual letters off that (using OpenGL).

The time needed to create the surface is still probably .0001 times that needed to load the font off disk though.

A "ChangeText" method is a good idea, but then you need one object of this class for every text you put on the screen (And thus different copies of the font), or call ChangeText for each one, which eliminates the benefits.

This topic is closed to new replies.

Advertisement