SDL2 Shooter 2 tutorial
SDL2 Widget tutorial
SDL2 Adventure tutorial
Orb source code
— 2D Shoot 'Em Up Tutorial —
Note: this tutorial builds upon the ones that came before it. If you aren't familiar with the previous tutorials in this series you should read those first.
We can shoot the enemies to destroy them, but right now they just vanish. That's no fun. How about we make them explode, instead? Unpack the code and then type make to build. Once compiling is finished type ./shooter11 to run the code.
A 1280 x 720 window will open, with a colorful background. A spaceship sprite will also be shown, as in the screenshot above. The ship can now be moved using the arrow keys. Up, down, left, and right will move the ship in the respective directions. You can also fire by holding down the left control key. Enemies (basically red versions of the player's ship) will spawn from the right and move to the left. Shoot enemies to destroy them. Enemies can fire back, so you should avoid their shots. Score points by destroying the enemy ships. Close the window by clicking on the window's close button.
Inspecting the code
We're going to be adding support for drawing text on screen. We're going to keep things simple for this tutorial and work with a bitmap font, extracting the glyphs from a texture. We'll also only be supporting a limited ASCII range - numbers, uppercase letters, and some symbols. The glyphs themselves are fixed width, so we can hardcode the width and height for the glyphs. This is a simple, quick solution and will suffice for now. Let's start with looking at defs.h:
We've added a new define to specify the maximum length of a character string. This will come into play when we're building our string to draw onscreen. Our buffer will be 1024 characters, plenty of room. Next, structs.h:
To Stage, we've added a variable to hold our score. Nothing special. Now onto the actual font and text handling code. For this, we've created a new compilation unit called text.c. There are just two function within - initFonts and drawText. First, initFonts:
Here, we're calling our loadTexture function to grab the font texture we want to work with. Nothing we've not seen before. The drawText function is a little more complex, but should be quite easy to get your head around:
There's a lot going on here, so we'll consider the parameters first. This function takes a minimum of 6 arguments: The x and y position that we want draw our text at; the colour of the text, in r, g, b; and the text itself, as format. You'll notice this function supports varargs. This is so that we can use text formatting (we'll see this in action later).
As our text glyphs exist on a single texture, we'll want to extract them for drawing. As we saw when we were doing the debris for the ships, we set up an SDL_Rect to specify the region of the texture to use. We'll be doing the same thing here. The first thing we're doing in our drawText function is clearing our drawTextBuffer (a static variable in text.c). After this, we're using C's varargs functionality to format our string. We're grabbing the length of the resulting string and then setting up our SDL_Rect's w and h values using our defined GLYPH_WIDTH and GLYPH_HEIGHT values that exist in text.h. Next, we're setting the colour that we want to draw our text with, by calling SDL_SetTextureColorMod.
We then create a for loop and step through each of the characters in our string. As mentioned earlier, we are only supporting certain ASCII characters. If the character we encounter falls outside of this range, we don't draw it. For a valid character, we setup our rect's x and y. We calculate the x by subtracting the int value of space (' '). This is because a space is the first character in our font texture and therefore has an x position of 0. This basically aligns our glyph coordinates on the x axis. As our glyphs are all on one line, we don't have to do anything with y other than set it to 0. With our rect calculated, we call our blitRect function and increment x by GLYPH_WIDTH so that the letters don't all render over the top of one another.
Phew! It seems complicated, but when you think about it, it actually makes good sense. Now let's look at stage.c, where we've put this all to work:
In resetStage, we're doing little more than resetting stage.score back to 0. We've also updated our draw function:
We're calling a new function called drawHud (which we're calling last, so that the hud info is drawn over everything else). drawHud is where we actually call our drawText function:
We want to draw two string here: one showing the player's current score and the other the highscore. In both cases we're making use of drawText's varargs. You can see that it works rather like printf statements, where we're specifying the format of the number. The first drawText function draws the player's score in white, on the left-hand side of the screen. The second call draws the highscore. To demonstrate the colour function, we're checking if the player has the current highscore and, if so, we're drawing the text in green. Otherwise, it's drawn in white.
Showing the score wouldn't be any good if we never incremented it. We'll update the bulletHitFighter function to award the player a point any time they kill an enemy:
We'll also set the highscore to the player's score if it's greater (using the MAX macro). Finally, we need to update main.c to initialize our text drawing functions:
We need only call initFonts to get our font rendering working.
Hurrah! We now have some text drawing capabilities (if somewhat limited). This will now allow us to create things like a highscore table, a title screen, and other pieces of information that could rely on showing text. Our game is currently quite easy, though: you'll probably rack up quite a score playing the game. What we'll do in the next tutorial is add points pods that the player will need to collect in order to up their score.
The source code for all parts of this tutorial (including assets) is available here:
It is also available as part of the SDL2 tutorial bundle (with on-going updates):