• 2D shoot 'em up
The Legend of Edgar 1.37
SDL2 Santa game tutorial 🎅
SDL2 Shooter 3 tutorial
The Legend of Edgar 1.36
SDL2 map editor tutorial [UPDATED]
— 2D Santa game —
It's time to give the player some more information as to what's going on. We need to be able to see our score, gift and coal supplies, and other data. A HUD is a stable of many games, and this one is no exception.
Extract the archive, run cmake CMakeLists.txt, followed by make, and then use ./santa07 to run the code. You will see a window open like the one above, with the scene moving from right to left. Use the same controls as before. As your earn points, your score on the HUD will increase. Additionally, when you deploy coal and gifts, those levels will decrease (but remain unlimited). Pay attention, too, to the Xmas Spirit bar on the right. If you fail to deliver a gift to a Nice house, the bar will decrease and visibly shudder. When you're finished, close the window to exit.
Inspecting the code
Once again, our HUD is quite trivial to implement, and other than some aesthetics there won't be too many surprises here; it should be quite simple for all to understand, so let's get straight to it.
First to defs.h:
We've added MAX_XMAS_SPIRIT, that will define the maximum value of Xmas spirit.
Now over to structs.h, where we've updated Stage:
We've added three new fields - xmasSpirit, that will hold the current level of our Xmas Spirit; numGifts, that will be the number of gifts the player currently has; and numCoal, which is the amount of coal the player has.
Now onto hud.c, the compilation unit we've created to handle all our HUD functions. Once again, there won't be too many surprises to come here; it's mostly just rendering strings at the top of the screen.
Starting with initHUD:
We're setting a variable called `score` here. This score is defined as a double, and is used to smoothly animate the counting up of our score display when we earn points. We'll see this in a moment. Next up, we're setting xmasSpirit to the value of Stage's xmasSpirit. Once again, this variable is a double, and will be used to smoothly animate the decrease of Xmas Spirit display. xmasSpiritBarShudder is a control variable to handle the Xmas Spirit bar shaking when it drains. xmaxSpiritBarWarningTimer will be used to flash the Xmas Spirit bar when it is low. All these variables are declare static in hud.c.
With our variables all setup, we can move on to doHUD, where we're processing our HUD's logic:
This is where we're adding up the scores and handling the Xmas Spirit bar shudder. To begin with, we're testing whether our local `score` variable is lower than Stage's score, and if so we're increasing its value (ensuring that we don't go over). Otherwise, we're setting `score` to be the same as Stage's `score`. Note that we're not counting down if we lose points. It's something that would be added easily enough if one desired, as we'll see next.
Our Xmas Spirit is doing much the same thing as our score display, except that we're decreasing the value of our local xmasSpirit if it's higher than Stage's. We're also increasing the value of xmaxSpiritBarShudder at the same time, to make the bar shake. If both xmasSpirit values are equal, we stop the bar from shaking.
Finally, we're always increasing the value of xmaxSpiritBarWarningTimer, to control how often we flash the bar, if needed.
Now for the rendering routine, so over to drawHUD:
For the most part, this is rather simple - we're just rendering the text of our score, gifts, and coal at various points across the top of the screen. This happens to be the reason why we're limiting Santa to 50 pixels on the vertical, since we don't want him to enter the HUD area.
When it comes to our Xmas Spirit, we're drawing a white rectangle of XMAS_SPIRIT_BAR_WIDTH length, and then drawing another rectangle that will have a width based on the percentage of Xmas Spirit we have remaining (using our local xmasSpirit, divided by MAX_XMAS_SPIRIT - for a value between 0 and 1 - and multiplying our width by the result). Using the local xmasSpirit, which is a double, this will cause the bar to smoothly decrease in length, rather than immediately jump to the new value. To make the bar shudder, we're drawing the bar at a vertical position (`y`) that will be adjusted by the sine of xmaxSpiritBarShudder, multiplied by 5. When it comes to choosing our bar's colour (`r`, `g`, `b`), we're setting a light blue colour, then testing if our xmasSpirit is low ( <= 1) and whether the modulo of our xmaxSpiritBarWarningTimer is within half a second, and altering the colour to white, before rendering the bar.
All pretty simple at the end of the day. Nothing we've not seen before, and just some added glitz.
Our HUD is done, so let's look at the other little tweaks we've made. First to player.c, where we've updated dropGift:
As we're now displaying the number of gifts and coal we have remaining, we're going to decrease the values of each in Stage when we drop them. Note how there's no limit set on these just yet, allowing them to go negative. We'll soon prevent drops if the player is out of stocks.
Next to chimney.c, where we've made a couple of other changes. First, to initChimney:
We're assigning the `die` function pointer to the new `die` function we've added:
This `die` function will be called by the doEntities loop in entities.c, before an entity is removed. Here, we're testing if this is a Nice chimney and whether it is incomplete (we never dropped a gift into this house, as we were supposed to). If so, we're going to decrease Stage's xmasSpirit. This means that whenever a chimney moves off the left side of the screen and its `dead` flag is set, we'll lose xmasSpirit if we missed a gift delivery.
We could, of course, have put this code into our `logic` function. The advantage here is that we could extend the game in future to cause other things to destroy the chimney, triggering the `die` function, and centralizing our response code.
We're nearly done, so it's over to stage.c to implement the final changes. First, we've added a new function called startStage:
Here, we're initializing various aspects of our stage, including the starting speed, Xmas Spirit, the amount of coal and gifts we have, setting up the player, and calling initHUD. We've moved these things into a new function as later we'll want to call initStage without setting these up (for example, when viewing the title screen / highscores).
Next up, we've updated doStage:
We've added in a call to doHUD here.
On to drawStage:
Here, we've added in a call to drawHUD.
Lastly, we've got to make one change to main.c, to account for our new startStage function:
We've added in the call to startStage just after initStage.
And that's it for our HUD and information display. A small update, but an important one. We can now see how many points we've earned, and how many gifts and coal we have remaining, and whether we're in danger of losing too much Xmas Spirit.
But what about the endgame? Our game is an endless scroller, but at some point we're going to lose (there is no "win" condition). It's about time we added in the endgame, in the form of a Game Over screen. So, in the next part we're going to implement the losing phase. It will be a bit unfair, as Santa won't have enough gifts to satisfy all the Nice houses, so will eventually run out of Xmas Spirit. But only if he doesn't crash into a house first!
The source code for all parts of this tutorial (including assets) is available for purchase, as part of the SDL2 tutorials bundle: