— 2D platformer tutorial —
Introduction 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. Extract the archive, run cmake CMakeLists.txt, followed by make to build. Once compiling is finished type ./ppp04 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. Close the window by clicking on the window's close button. Inspecting the code Allowing Pete to interact with other entities is quite a simple task. First, we should define some flags to help with our collision detection and response. In defs.h we'll add a few:
These will be used to specify things like whether the entity is affected by gravity and if it's solid (meaning that it will block movement). With those done, we can move onto entities.c and make some changes. The first function we'll update is move:
Right away, we're testing whether our entity is weightless. If it is, we're not applying gravity to it, meaning that it will remain suspending in the air (unless it is told to move all by itself). Next, we want to update our movement steps. We've now got a new function called moveToEntities, which will take the same parameters as moveToWorld. This function is used purely for moving and interacting with entities in the world. It's not a hugely complicated function, especially compared to moveToWorld, and actually takes some cues from it:
We're stepping through all the entities in the stage and checking to see if our subject has collided with any of them (first checking to see that the subject and target are different). If a collision does take place, we're then checking to see if the other entity is solid. If so, we're checking the direction the entity was moving in and aligning them to the correct side of the target (left, right, top, or bottom). This response is much the same as what we were doing when testing against the world. The only major difference here is that the subject will be aligned according to the target entity's width and height, rather than TILE_SIZE. In all cases, the subject's dx and dy is zeroed during a collision, and the isOnGround flag is set if they are were moving down when the hit occurred. This works perfectly, and allows Pete to stand and walk on solid entities as if they were a part of the world map. This could be used to create things like crumbling blocks, bridges, doors, etc. With that done, let's take a quick look at how we're loading and setting up other entities in the world. A couple of new functions have been added to entities.c - loadEnts and addEntFromLine. We'll look at these in order:
loadEnts reads a file into memory using the readFile function and then fills a char array called line with a line from the data. It's not hugely effecient, but gets the job done. Once the \n character is hit (specifying a line break), the line array is passed to a function called addEntFromLine. The line array is then cleared and character data is read into the array until the next line break is hit (or until the end of the data is encountered). The addEntFromLine function is quite simple:
An entity is created and then the entity name in the line passed over is read, using sscanf. A strcmp is called on the name to determine the type of entity. If it's "BLOCK" we create a block entity, loading the texture we want to use, setting its size, and then adding the EF_SOLID and EF_WEIGHTLESS flags to it. There is a small issue here that an unknown entity will set up nothing and perhaps even crash later, due to things like missing textures, etc. For now, BLOCK is the only entity we're supporting, so it's not a huge deal. In a full game, error checks should be performed across this step to reject unknown entities. Before we finish, let's look at the format of the entity data in the file: BLOCK 896 512 BLOCK 1728 448 BLOCK 1664 320 ... As you can see, each line contains the entity identifier, the x coordinate, and y coordinate in the world. Adding more entities would simply be a case of adding more data to the file and supporting it in code. Our game is making great progress. We can load the map and entities, and traverse both. What we should do next is support moving platforms. They should carry Pete around when they move, both up, down, left, and right. Purchase The source code for all parts of this tutorial (including assets) is available for purchase: From itch.io It is also available as part of the SDL2 tutorial bundle: | |
Desktop site |