Saturday, August 18, 2012

Lesson 6: True Type Fonts with SDL_ttf

Deprecation Notice:

I've been updating the lessons now that SDL2 is officially released, please visit my new site on Github that has updated lessons and improved code.


In this lesson we will start learning how to use the SDL_ttf library to render text using True Type fonts. For this lesson you'll need the SDL_ttf library which you should have from when you downloaded and compiled all the libraries on SDL2.0 Mercurial. If you don't have the SDL2.0 version of SDL_ttf, you should download and compile it now. The library is linked up the same as SDL_image was in Lesson 3.

In addition to the library we'll need a true type font to use to draw our text. It's important that you're careful when choosing a font as many fonts are proprietary or have rules about using them in software, and if used without the proper licensing can result in a nasty legal mess. Fortunately for us there are many excellent open fonts available online. For this lesson we'll be using a set of open source fonts recently released by Adobe which you can download directly from sourceforge, the specific font I chose for this lesson is SourceSansPro-Regular, but you can use any font you like.

 Now that we've got our font downloaded and libraries linked up lets take a look at how we can work with fonts via SDL_ttf. The library provides functionality for opening and closing TTF fonts and rendering text to SDL_Surface pointers. The library also lets us specify the color and font size we'd like to use when rendering the text. You can find the full library documentation here.

First we'll need to add SDL_ttf.h to our includes:
In order to use the library we need to initialize it in our main function after initializing SDL. Note that we check for errors similar to how we check for issues when starting SDL, except that now to get the appropriate error information we use TTF_GetError, similar to what we did in Lesson 3 to get SDL_image error information.
Next we'll want to make a function to render a message and abstract away the work that goes into it, and simply return an SDL_Texture that we can draw to the screen. In addition to rendering the message the function will also need to convert the SDL_Surface pointer returned from TTF_RenderText_X to a SDL_Texture pointer and return that.

The information needed to render the message is the message itself, a font file name, a font size and a font color. We can now add a nice function to render our desired message to our code:
That's quite a bit of code! Lets step through it and figure out exactly what's going on. First, we know that we need a message, font file, font color and font size so that we can properly draw our message. SDL provides the SDL_Color structure that is used for specifying what color we want and takes three values for RGB which can each range from 0-255.

The first step in drawing the text is to open our font, which we do on line 4 using TTF_OpenFont, which takes the font file and the font size to open the font as. Note that the same nullptr error checking that we've used before in LoadImage is used to make sure the font opened ok. If it fails we throw an error and include the font file name that caused the error along with TTF_GetError so that we can have enough information to diagnose the issue.

If the font loaded ok we use TTF_RenderText_Blended to write the message to an SDL_Surface and get the pointer back. There are a few methods provided by SDL_ttf for rendering text, blended is the slowest but provides a nice smooth look. The other methods can be found in the documentation linked above.

However because we use SDL_Textures in our rendering we need to convert the surface over. This isn't too difficult because we've made our SDL_Renderer a global and so it's easy to pass it into the function SDL_CreateTextureFromSurface. After creating the SDL_Texture we need to clean up things we no longer need to keep in memory, the SDL_Surface and the TTF_Font. We free the surface the same as before and can use TTF_CloseFont to close the font we opened.

Once everything is cleaned up we return the SDL_Texture pointer for usage in the program.

When we want to render some text in our program we can now perform a call like so:
Here we render the message "TTF fonts are cool!" using the font we downloaded above in white with a font size of 64.

You can then treat the texture as any other texture and draw it the same as we've done previously with our ApplySurface function. If you draw the message centered, you should see something like this when you run the program:
Try playing around with the other TTF_RenderText_X functions and see how they look!

End of Lesson 6

Thanks for joining me! I'll see you again soon in Lesson 7: Taking Advantage of Classes.

Thursday, August 2, 2012

Lesson 5: Clipping Sprite Sheets

Deprecation Notice:

I've been updating the lessons now that SDL2 is officially released, please visit my new site on Github that has updated lessons and improved code.


It's very common in sprite based games to use a large image file containing many smaller images, such as tiles for a tileset, as opposed to having a separate image file for each tile. This type of image is known as a sprite sheet, and is very useful because we don't need to have a separate image file for each image in the game but instead can just draw the subset of the sheet that we want.

In this lesson we will learn how to use a simple sprite sheet in our program and how we can specify the desired subset, known as a clip, to draw. The sprite sheet for this lesson is a simple one containing four circles:
In a sprite sheet images are divided up into sections of some sort of basic shape that we can clip. In this sheet each circle is within a 100x100 rectangle that we can pick to draw, instead of the entire image.

The code from this lesson will be building upon the code from Lesson 4, if you haven't done Lesson 4 you should head back and do it. If you already know the material covered in Lesson 4 grab the code and let's get started.

Within our ApplySurface function from Lesson 4 you'll notice that there's one last NULL value being passed to SDL_RenderCopy.
This parameter is the source rect, more commonly known as the clip, and specifies a sub-rectangle of the image to draw, ie. the clip's position, width and height. In order to pass a clip for the image we'll want to add a clip parameter to ApplySurface.

But what if we had an image where we did want to draw the whole thing? Instead of forcing ourselves to pass a clip of the whole image, we can instead specify a default parameter and detect when we get it. We'll take the clip parameter as an SDL_Rect pointer with a default value of NULL, that way if no clip is passed we can still pass the parameter to RenderCopy and the call will be the same as it was previously when we passed NULL to draw the whole image.

We'll also want to add one more thing to our function: If we pass a clip parameter we clearly would want to use the clip's width and height as our destination values instead of the whole texture's width and height. If we used the texture's width and height our clip would be stretched to match the size of the original texture.
We'll load our image the same as before with or LoadImage function.

We now need to set up our clip rects, which we'll store inside an array. Instead of typing all of our clips out by hand which is very tedious for images with lots of clips, we can take advantage of the pattern in which our image file is constructed to automate the creation of the clip rects with a for loop. If you did the extra challenge from Lesson 2 this method should look quite familiar.

First we specify the width and height each clip should be, in this case 100x100 and we create an array to store four SDL_Rects to hold our clips. Next we use our knowledge of the arrangement of our clips to create a for loop with a column counter to appropriately set the x,y coordinate for each clip.
If you didn't do the Lesson 2 challenge or are feeling a bit lost, let me explain how this works. We want to create four clips, so we set our loop to run four times, from zero to three to match the array indices. We also need to track which column we're going down so that we can correctly set the x coordinate for our clip, we'll begin with column zero and will increment the column value when we move over, which would be at the third clip, ie. i % 2 == 0 since we start counting from zero. We also want to make sure we don't increment column when we first start out, because 0 % 2 is also 0, so we put a condition to ignore that case.

Now we'll want to calculate the x and y coordinates for our clip in the image. We want to increment the x coordinate by the image width each time we move over a column, and increase the y each time we move down one in the column. The x coordinate setting should be clear, we can just use tile width * column. The y coordinate setting uses our current loop iteration mod the number of clips per column to determine which row we're on. We then multiple this row number by the tile height to get the location in pixels. The width and height are uniform for all clips so we can just set them, without any extra calculations.

Thus when we run this loop we will create four clips and each clip will take the appropriate coordinate for the sub-rectangle. Still not sure how it works? Try running the for loop in your head and calculating the column, x and y values for each iteration and see where they line up on the sprite sheet.

The last step is to set a value which can track which clip we want to draw, in this case an integer corresponding to the clip's index in the array. Let's begin by drawing clip 0.
Before beginning our main loop we'll also need to set an x and y position to draw the image at. I chose to calculate the center position for the image using the same method as in previous lessons. We'll also want to create a bool quit variable and an SDL_Event.

In order to make sure our clips were set correctly and display correctly we'll want to be able to draw each one individually, to do this we can set up some event polling to change the value of useClip so that we can draw each clip.
Here we check if the input type is a key down event, and then use a switch statement on the key symbol to pick the appropriate response.

Finally in our render section we clear the screen, pass the clip's value to our ApplySurface function in the rendering section of our loop:
And finally, present the renderer to display the changes.

When you run the program you should be able to push the number keys 1-4 and see the different colored circles appear individually on screen!

End of Lesson 5

Thanks for joining me! I'll see you again soon in Lesson 6: True Type Fonts with SDL_ttf.