• 2D shoot 'em up
SDL2 turn-based strategy tutorial
Water Closet ported to PlayStation Vita
The Legend of Edgar 1.35
SDL2 Rogue tutorial
— 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 ./shooter12 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 collect points pods released from destroyed enemy ships. Close the window by clicking on the window's close button.
Inspecting the code
To make the game a little more challenging, we're going to make it so that the player only gains points by collects points pods that are released by destroying enemy ships. The means that the player will be forced to negotiate enemy fire while hunting them down. Let's begin with structs.h:
We've added a linked list to Stage, to allow it to hold our points pods. Nothing more to say here. Moving on and we see that, as always, the bulk of the changes have been made in stage.c. Starting with initStage:
We start by loading the texture that we'll be using to draw our point pods with (pointsTexture). Then, we reset our points pods linked lists in resetStage:
With that done, we add a new function call to logic, telling it to call doPointsPods.
This function will handle our points pods. We'll see more on this later on. We next update bulletHitFighter:
This update will call a new function called addPointsPod. It will pass over the x and y coordinates that the points pod should spawn from. Note that we're passing over the destroyed fighter's midpoint. Also note that we're only doing this for enemy fighters, so that we don't see the player release a points pod when they are destroyed.
Now let's consider our new doPointsPods routine:
It appears that plenty is going on here, but it's a little more straightforward than that. We step through our linked list, handling each points pod in turn. First, we check to see if the points pod's x has gone off screen. If so, we reverse the pod's dx. We do the same check with the pod's y, reversing the dy if needed. This will cause the pod to bounce, making things more interesting. We then add the pod's dx and dy to its x and y respectively, to move it. With the movement done, we then check to see if the pod has made contact with the player. We first ensure player isn't NULL and then call our collision function, passing over the player's and pod's x, y, w, and h. If contact is made, the set the pod's health to 0, increment stage.score, update highscore if needed, and finally play a sound. At all times, a points pod's health will be decremented and the pod deleted if health hits 0 or less.
That should actually all be quite easy to understand. Equally, addPointsPod should be very simple:
Our points pod is an Entity. We malloc one and add it to our linked list, then set its x and y to the coordinates passed into the function. We then set the pod's dx to be a random value between 0 and -4. This means that in most cases the pod will move from right to left. We then set the pod's dy to a random between -5 and +5, causing it to move up or down the screen at a random speed. Remember that when the pods touch the sides of the screen their velocity will be reversed, causing them to bounce. The pod's health is set to FPS * 10, making them live for 10 seconds before vanishing. We next set the pod's texture to pointsTexture, grab the width and height of the texture, setting them to the pod's w and h using SDL_QueryTexture, allowing our collision detection routine to work. Finally, we make one small adjustment to the pod's x and y, shifting each one by half the pod's w and h to better center them.
Now for drawing. This won't be too complicated, at all. We update our main draw function, adding a call to drawPointsPods:
Note that we're drawing the points pods after drawStarfield, quite early on in the whole scene. This means that the points pods won't obscure things like bullets, explosions, and fighters. The last update in stage.c is the actual drawPointsPods function:
Very, very simple. We just step through our linked list and draw all the pods.
Before we wrap up, let's look at a couple of other things that have changed. defs.h has seen a couple of new additions:
We add a new sound channel to play our points sound through (so that it doesn't get cut off by things like explosions and the sound of firing). We also declare SND_POINTS into which we'll load our sounds. This can be seen in sound.c:
And that's a wrap! The game is now more fun and challenging to play. It's no longer possible to rack up dozens of points, making achieving a highscore a matter of skill. It would be good to log all these attempts at a highscore. In the next tutorial, we'll do just that: introduce a highscore table.
The source code for all parts of this tutorial (including assets) is available for purchase:
It is also available as part of the SDL2 tutorial bundle:
If you do not wish to create an itch.io account, you can also purchase the tutorial bundle using PayPal, and then download the tutorials directly from the main tutorials page.