![]() | |
PC Games
• Orb Tutorials
• 2D shoot 'em up Latest Updates
SDL2 Quest game tutorial
SDL2 Versus game tutorial
Download keys for SDL2 tutorials on itch.io
The Legend of Edgar 1.37
SDL2 Santa game tutorial 🎅
Tags • android (3) • battle-for-the-solar-system (10) • blob-wars (10) • brexit (1) • code (6) • edgar (9) • games (44) • lasagne-monsters (1) • making-of (5) • match3 (1) • numberblocksonline (1) • orb (2) • site (1) • tanx (4) • three-guys (3) • three-guys-apocalypse (3) • tutorials (18) • water-closet (4) Books ![]() A North-South Divide For over a hundred years, messenger Duncan has wandered the world, searching for the missing pieces of an amulet that will rid him of his curse; a curse that has burdened him with an extreme intolerance of the cold, an unnaturally long life, and the despair of watching all he knew and loved become lost to the ravages of time. But now, Duncan is close to the end of his long quest. |
— Simple 2D quest game — Note: this tutorial assumes knowledge of C, as well as prior tutorials.
Introduction We can now fully explore our overworld, sail boats, and find towns scattered about. But what we would really like to do is to be able to visit these town properly. So, in this part we're going to put together the foundations (pun intended) for exploring the towns themselves. To begin with, our town will be very spartan; it will consist of nothing more than the parameter, and an entrance. We'll add the buildings, doors, and NPCs in subsequent parts. Extract the archive, run cmake CMakeLists.txt, followed by make, and then use ./quest07 to run the code. As before, you can move around with the WASD control scheme. As already stated, there is no much to do. The walls bordering the town will restrict the player's movement. Otherwise, this is a empty map. When you're finished, close the window to exit. Inspecting the code While the gameplay itself might not represent much, there is a lot happening under the hood that we'll need to look at. This part is largely about putting everything together to support exploring our towns, with the next parts being able to build upon it, and add features. First up, we've made changes to structs.h. We've added in a Town struct:
Our Town struct will hold our town's `map`, and location of its `entrance` (where the player will be placed when they enter the town), and a reference to the `entity` on the overworld itself (we'll make use of this later). This Town struct will be used as the town entity's `data`. Next up, we've made a tweak to Game:
We've included a pointer to the Town entity (`town`) that the player is currently in. This will be used to determine whether we want to handle the logic for moving on the overworld or within a town. It will also be helpful later, when we come to add in the HUD, etc. Now over to the first of the two major new additions. townGen.c and town.c are responsible for creating a town and navigating it, respectively. We'll start with townGen.c, so we can see how a town is created, and then look at how its logic is handled when the player is within it. First up, we have generateTown:
This function is the entry point to our town generation. Note that it accepts an entity (`e`) as a parameter. This is the Town entity on the overworld map, that our created Town data will be linked against. We start by creating a Town object (`t`), assigning it to the entity's `data` field, and then setting Game's `map` pointer to the town's `map`. A reminder that we do this because our game's code works within a map context. All operations such as creating entities will now happen within the context of Town's `map` (Game's `map`). We then invoke various functions, to create our town. We'll go through these one by one, starting with prepareMap:
Just like our overworld map, we're using a malloc call to create map data of the required size (TOWN_WIDTH * TOWN_HEIGHT) for our Town. Nothing more to add here. So, over to addGround:
Another basic function. We're looping through our Town's `map` data, and testing if the current `x` and `y` point is part of the town's border. If so, we're setting it to be a town wall tile. Otherwise, we're making it a ground tile. Moving on now to something a bit more interesting. addEntrance is where we create the entry way for the town:
In a nutshell, what this code is doing is selecting a random point along the border of the town, and converting the surrounding tiles there into ground tiles. When selecting the point, we ensure that it is away from the corners of the town, as well as the edges themselves, so our for-loop doesn't need to perform bounds checking. Carving out a square area in this way also means that we don't need to concern ourselves with whether our entrance is horizontal or vertical. With our entrance carved out, we set the Town's `entrance` location to the main entry point we determined (`mx` and `my`). Finally, we have the addAdventurer function:
Nothing out of the ordinary here - we're creating the Adventurer entity, and then placing them at the town's `entrance`. We're then setting the town's `map`'s `player` pointer to the adventurer (remember that each Map will have its own player pointer, and effectively its own Adventurer entity). That's all there is to townGen.c for now; we'll be heavily expanding it in future parts. Now, let's look at how we're handling our town logic itself, over in town.c. First up, we have initTowns:
As with the overworld, towns will have their own graphics set, so we need to load them with a call to loadMapTiles. Next up, we have enterTown:
This is a very important function, as it is what causes us to enter the town and explore it. Ultimately, it will be invoked by interactions on the overworld with town entity, hence why we pass an entity (`e`) into the function. We set Game's `town` and `map` to be the town data, then decorate the map (much the same way as we do with the overworld), and then set the `logic` and `draw` delegates. Again, a simple, but very important function. Next up, we have the `logic` function:
Little to say here, as right now it works much like the `logic` function for the overworld. We're processing our player, our entities, and the camera. The `draw` function is also quite simple:
We're just drawing our map tiles, and our entities. loadMapTiles is much like the function we have for the overworld:
... while `decorate` is also very similar in nature:
So, we can now generate our town, and also drive its logic. All very easy to understand. What we need to do next is update player.c, since there are a few differences between navigating the overworld, and navigating our town. We don't have boats or water in towns, for example. So, let's start by making a change to the `move` function:
Our entity interaction check remains the same. However, we're then checking if we're within a town, by checking Game's `town` pointer, and if not we're calling moveOverworld as before. However, if we're in a town, we'll call moveTown:
This new function will handle our player character moving about the town. Much like our overworld movement checks, we're dividing the target tile's data point by TILE_TYPE_RANGE, and then testing its value, to see if we're able to enter the tile (`t`). In this case, we're blocking movement into walls. This part is almost done. We just need to make a few more tweaks, and our town exploration will be ready. First off, we need to actually generate the town itself, when creating the world. We do this by updating the addTowns function in overworldGen.c:
Now, after creating our town entity (as Settlement), we're passing it over to the new generateTown function. Finally, we've added a temporary function to overworld.c, called enterFirstTown:
This function is invoked by enterOverworld, essentially overiding its behaviour. As you can see, we search through all the overworld entities, looking for a Town (ET_SETTLEMENT). The first one we'll find will be passed to the enterTown function, moving the gameplay there. As we'll see in a future part, this is the type of logic that will be invoked when a player makes contact with a town entity in the overworld (via its `touch` function). And our first steps to entering towns is done! Running the game, it might not seem like a lot, but once again we've done a lot to support exploring towns. The parts to come will emphasis this further. So, we can explore our town, but it's pretty empty, aside from the surrounding walls. What we need to do is add in some buildings, that the player can enter. Therefore, in the next part we'll look into doing just this. 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 |