— 2D Santa game — Note: this tutorial assumes knowledge of C, as well as prior tutorials.
Introduction Up until now, we've had no means of increasing our supplies of gifts and coal. This has meant that for the past couple of parts we cannot help but fail the game once we run out of gifts. In this part we're changing that all, by allowing the player to collect sacks containing gifts and coal, that will restock our supply. This will allow the game to keep going for far longer than before. Extract the archive, run cmake CMakeLists.txt, followed by make, and then use ./santa10 to run the code. Press Space to play. Use the same controls as before. As you play, coal and gift sacks will appear in the air. Fly into them to collect them and increase your supplies. The game will continue for as long as you are able to maintain your Xmas Spirit (and don't crash into a house). When you're finished, close the window to exit. Inspecting the code Adding in our sacks is a very simple thing indeed; they're just entities that we spawn at random intervals and can be collided with. First, to defs.h:
We're defining two new entity types: ET_GIFT_SACK, to represent a sack containing gifts, and ET_COAL_SACK, to represent a sack containing coal. Next, to structs.h:
We've created a new struct called Sack, to hold the data about one of our sack entities. `startY` is the vertical starting position of the sack. `bob` is the value of the bobbing amount of the sack, used to control the rate at which it rises and falls; and `speed` is the horizontal speed at which the sack moves across the screen. We're giving them random speeds to keep things interesting. Now to sack.c, the new compilation unit we've made to handle our sack logic and rendering. Once again, there won't be anything here that you will find taxing; it will be as straightforward as already described. Starting with initSack:
A standard entity spawning routine. The initSack function takes a parameter called `type`, that is the type of sack we want to make (we're assuming here we're only ever going to pass in ET_GIFT_SACK or ET_COAL_SACK - if we wanted to make this safer, we could have seperate functions calling initGiftSack and initCoalSack that would delegate to this function). We load our textures, and then create a Sack (`s`). The `speed` to set to a random between 0.5 and 1.4, while the startY is somewhere around the upper half of the screen. The entity itself is then created, with its `x` position being somewhere towards the right-hand side of the screen (a minimum of halfway through). The appropriate texture is assigned, depending on whether this is a gift or a coal sack, as well as the `tick`, `draw`, and `touch` functions. Again, nothing surprising. Our `tick` function follows:
Pretty standard. We're first increasing the value of sack's `bob` by a small amount, and then setting the entity's (`self`) `y` to the sack's startY, plus the sin of its `bob`, multiplied by 16. In effect, this will make it move up and down a little, about the `y` position where it was first created. We're next decreasing the sacks's `x` value according to both its speed and Stage's `speed`, so that it moves from right to left. Again, this means our sacks will move at various different speeds. Finally, we check if the sack has moved off the left-hand side of the screen, and setting its `dead` flag to 1 if so, so that our entity processing loop removes it. The `draw` function is up next:
We're just rendering the entity, using its `texture`. Now for `touch`, the most essential part of the sack's handling:
We're doing just what one would expect - we're testing if the thing that has touched the sack is the player, and if so we're going to increase the value of Stage's numGifts or numCoal depending on whether this is a gift or coal sack. We're increasing the amount by a random value of between 5 and 9. With that done, we set the sack's `dead` flag to 1, so that it is removed. A very simple, but very important function, since it now allows the player to continue to play, so long as they collect gift sacks. That's it for sack.c. In order to make use of it, we need to update stage.c, to spawn our sacks. Starting first with initStage, where we've made preperations:
We have a new static variable in stage.c called objectSpawnTimer, that will govern how often sacks are created. Here, we're setting the spawn time to be between 5 and 9 seconds upon starting. Next, we've updated doStage:
We're testing first if the game is in progress (Stage's `state` is SS_PLAYING), and then calling addObject. We don't want to be creating sacks (or any of our other interesting objects) while we're on the title screen. The addObject function is last:
This function will decrease the value of objectSpawnTimer, and then attempt to create an object when the value falls to 0 or less. To create our objects (right now, we only have sacks), we're testing a random of 100 (assigned to a variable called `n`). If it's less than 15, we'll call initGiftSack. If it's less than 30, we'll called initCoalSack. Any other number will be ignored, for now. Finally, we reset objectSpawnTimer back to a value between 5 and 9 seconds. And that's all there is to it! Our game is now fairer and players can enjoy it for much longer. How long they can keep going will depend on their skill in delivering gifts as the game speeds up, and also on the random chance of gift sacks appearing. Otherwise, one could argue that our game is now complete. But we don't want to leave it here, as there's so much more we can do to make things interesting. You might recall that we said our elves were responsible for teleporting in the sacks for Santa, but that their magic has gone slightly wrong and had enchanted the nearby snowmen? Well, in the next part we're going to introduce the first of these snowmen, who will act as hazards that the player must avoid. Purchase The source code for all parts of this tutorial (including assets) is available for purchase, as part of the SDL2 tutorials bundle: From itch.io | |
Desktop site |