It’s common in sprite based games to use a larger image file containing many smaller images, such as the tiles for a tileset, instead of having a separate image file for each tile. This type of image is known as a sprite sheet and is very handy to work with since we don’t need to change which texture we’re drawing each time but rather just which subsection of the texture.
In this lesson we’ll see how to select subsections of textures using
SDL_RenderCopy and also see a bit on detecting specific key press events, which we’ll use to pick which section of
the texture to draw. The sprite sheet will contain four different colored circles:
A sprite sheet can be built up of many uniform sized sprites, in which case clipping is easy, or many different sized sprites. To handle a sheet with varying sized sprites we’d need a metadata file that contained the location information for the clips. For this lesson our sheet has 4 100x100 sprites. The code for this lesson builds off of lesson 4, if you don’t have the code to build on top of grab it from Github and let’s get started!
Selecting the subsection of the texture that we want to draw is very easy with SDL. In Lesson 4, the remaining NULL
parameter that we’re passing to
SDL_RenderCopy is the source rect,
which specifies the section of the texture we want to draw. When passing NULL we specify that we want the entire texture,
but we can easily add a clip rect and start drawing only portions of the texture. To do this we’ll be making
some modifications to our renderTexture function to make it able to take a desired clipping rect, but still
keep some of the shorter syntax and conveniences from before.
To keep from tacking on more and more parameters to our renderTexture function and still maintain the convenience
the defaults provided we’ll split it up into two functions. One is practically an identical call
SDL_RenderCopy but provides the clip parameter with a default
nullptr value. This version of renderTexture will take
the destination as rect instead, which we can setup ourselves or have constructed by one of our other specialized
renderTexture functions. The new base rendering function becomes very simple.
It was also really nice when we didn’t need to create an SDL_Rect for our destination but could just provide
an x and y position and have our rendering function fill in the width and height of the texture. We’ll create
an overloaded version of renderTexture that will do this, with some tweaks to handle clipping. We’ll add
the clip rect as a parameter with
nullptr as a default value and in the case that a clip was passed, we’ll use
the clip’s width and height instead of the texture’s width and height. This way we won’t stretch a small
sprite to be the size of its potentially very large sprite sheet when it’s drawn. This function is a modification of
our original renderTexture function, and should look pretty similar.
In our case it’s very easy to automatically compute the clipping rectangles, using a method almost identical to the tiling method from Lesson 3, however instead of going row by row, we’ll go column by column. This way clip one will be green, two is red, three is blue and four is yellow. The idea behind the math is the same as in Lesson 3 but switched to wrap on columns. So our y coordinate is calculated by modding the tile index with the number of tiles (2), while the x coordinate is taken by dividing the index by the number of tiles. These x and y coordinates are the x and y indices, so we then scale them to the actual pixel coordinates by multiplying by the clip width and height, which is uniform (100x100). Finally we pick a clip to start drawing, in this case the first one.
We also would like to draw our clips in the center of the screen, so we compute those x and y coordinates using the clip’s width and height instead of the texture’s.
If instead we were using some more complicated sprite sheet with rotated and different sized sprites packed together we would need to store their location and rotation information in some kind of metadata file so that we could find the clips easily.
In order to examine all the clips we’ve created we’ll add some key input handling to our event loop and will
make the keys 1-4 select which clip we want to display. To determine if a key press happened we can check
if our event is of the type
SDL_KEYDOWN and if it is we can check if the key pressed was one of the keys we’re interested
in by checking the keycode information in the event,
A full list of event types, key codes
SDL_Event information is available in the wiki.
When we receive the key input we’re interested in we’ll need to update the value of useClip to match the clip we want to draw. With these additions our event loop will become:
The final thing to do is get the clip we want on the screen! We’ll do this by calling our more convenient version of renderTexture to draw the clip without any extra scaling and passing in the clip we want to use (the one at useClip).
When you run the program you should see clip 0 (green circle) draw in the center of the screen and should be able to select different clips to be drawn with the number keys. If you run into any issues double check your code and/or post a question below.
I’ll see you again soon in Lesson 6: True Type Fonts with SDL_ttf!