SDL2 Shooter 2 tutorial
SDL2 Widget tutorial
SDL2 Adventure tutorial
Orb source code
— 2D platformer tutorial —
Note: this tutorial series builds upon the ones that came before it. If you aren't familiar with the previous tutorials, or the prior ones of this series, you should read those first.
This tutorial will explain how to add and control a character on a basic 2D map. Unpack the code and then type make to build. Once compiling is finished type ./ppp05 to run the code.
A 1280 x 720 window will open, with a series of coloured squares displayed over a light blue background. Pete, the player character, will drop to the ground from the top-left of the screen. He can be controlled using A and D to move him left and right, and using I to jump. Pressing Space will reset Pete to the starting point. If you're not fond of these controls, they can be easily changed in player.c. Black squares with white outlines are entities that Pete can stand on. Moving platforms will carry Pete around if he stands on them, and push him out of the way if he attempts to block them. Collect pizza slices by walking into them. Close the window by clicking on the window's close button.
Inspecting the code
We're going to forego adding a title screen and highscore table this time; there is only one level and no real way for Pete to lose a life, so we'll be jumping straight into the game. We're also going to be skipping over drawing text and playing sounds, as these were covered in earlier tutorials.
We're going to start with looking at the changes to structs.h, and the Entity object:
We're adding in a health variable and a function pointer for touch, and also a variable called value. Every Entity created will need to have at least 1 health point. An entity with <= 0 health will be removed. We'll see this in a bit (shouldn't come as any surprise to those who have been following these tutorials before). We're also updating the Stage struct to add in some data about the pizza slices that are available on the stage:
The pizzaTotal and pizzaFound variables will be used to give guidance to the player as to their progress. This information will be displayed on the hud. Now let's look at the updates to entities.c. The first update to doEntities is rather routine:
As can be seen, we're testing to see if the entity in question has <= 0 health and deleting it if so. Nothing special. Again, we just need to ensure that when an entity is created we give it at least one health point so that it is not immediately removed from the world. This includes Pete, platforms, blocks, etc. The next update is to moveToEntities. This is where we'll be making use of the touch function:
After two entities had collided with each other, we are calling the touch function (if it's set). We pass the reference to the entity that we hit (other) over to the one that made contact. As this is a function pointer, we can define whatever action should be taken. Something to note is that this touch function may be called more than once when an entity moves. This is because we move on one axis at a time. For this tutorial, it's not a big deal. However, it might be prudent for a larger game to make a note of the unique contacts that were made and deal with them just once.
The final update to entities.c involves adding the pizza slices, in addEntFromLine:
If while reading in the entity data we encounter a line that starts with PIZZA, we'll call a new function called initPizza. This function exists in a new file called pizza.c:
The function is quite simple: it just creates an Entity and sets all its relevant details (including setting the health to 1!). We also increment stage.pizzaTotal for each slice created, so we can inform the player how many slices are available on the level. We want our pizza slices to float, so we make them weightless by setting flags to EF_WEIGHTLESS. We also set the pizza's tick and touch functions. Starting with tick:
We're increasing the pizza's value variable by 0.1 per frame and then using the sin of that amount to adjust the pizza's y value. The entity's value variable is just a general purpose variable that we're abusing; it could be used for a number of other things. By adding the sin of value to y, we cause the pizza slice to bob up and down on the spot. This looks nicer than them just sitting there. The touch function is more interesting:
We want to test what's touched our pizza. If it's the player and the pizza slice is still alive (health > 0), we will be collecting it. The pizza's own health is set to 0 and the number of pizza slices found is incremented. We then check to see if we've found all the pizza slices or still have some remaining, and play the appropriate sound. We could do anything we want in this touch function, such as creating more pizza slices, ending the level, trigger an event, etc.
Finally, let's take a quick look at the drawHud function. It lives in stage.c and is rather simple:
All we're doing is creating a transparent black strip at the top of the screen and overlaying some text. We're doing this by setting up an SDL_Rect, calling SDL_SetRenderDrawBlendMode with SDL_BLENDMODE_BLEND to tell the renderer to draw alpha colors, setting SDL_SetRenderDrawColor to a transparent black, and then calling SDL_RenderFillRect to draw our black strip. With that done, we're calling drawText, setting the appropriate location and color, and outputting the pizza stats from stage. You will see the pizzaFound counter increment as pizza is collected.
And there you have it: a basic, but playable, platform game. This game could easily be extended to feature larger levels, more objects and items to collect, and more things to do. Hopefully, you'll have found this useful as the basis for creating your own platform game.
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):