My Turn-Based project is slowly moving along. You’ve been able to see most of the stuff I’m doing in the new video series, but since I want to keep those interesting and short, I’m not recording everything I do. A lot of the game development stuff is just trying out stuff, and rewriting/improving existing code.
One major part of the turn-based game is the level-generator. If you’ve been reading my previous blogs for previous games, you know I love creating procedural-level generators for all my games. One reason is that it allows me to create much more game content if needed, but the other reason is that it’s just awesome to code generators that surprise me in my own game. You should try it some day!.
How it works
I wanted to explain how the generator in this game works, mainly cause I love reading about such stuff and I’m sure it could be useful to somebody else to read about my ventures into this.
This level generator’s main function is to create a space-ship-complex-type-level. What that means, in my mind, is a lot of rooms with corridors between them, and doors. It’s also less organic, since it’s man-made, so it’s all very straight, rough edged, and cornered.
The first step the generator takes is create a random sized room of average size (minimum size 3×3, maximum size 6×6). Actually, the first step it does is make the whole world (a tilemap) solid. So all tiles are “wall” tiles and can’t be moved through. Now don’t yet think about graphics, just think of it as a 1 or 0. The 1 is a wall, the 0 is empty space. That’s all the generator has to think about at this point.
So after randomly deciding on the first room location and size, we cut out that area in the tilemap, and turn all the tiles into zero’s. We now have an open space, also known as: a room.
The second step we take is pick a random direction: up, right, down, or left. We then pick a random spot in the previous room’s wall to start a corridor. So let’s say our random direction is up, we pick a sweet spot in the top-wall of the first room. We then decide on the length of the corridor. I like to keep the corridors short, so I pick a random value between 2 and 6 tiles long.
We then decide on the size of the next room on the other end of the corridor, and now we have, in theory, the location and size of the next room and the corridor to connect to it. But we don’t carve it out yet. We now check if the area of the new room+corridor is still solid. We also check on 1 tile wider (in all directions) than the new room and corridor will be.
This way we make sure we don’t cut out a wall between a previously created room, and our new room or corridor, cause that could potentially break the “single-path” we wish to have in the game. Imagine placing your corridor alongside the wall of an existing room, it would make the corridor part of the room.. not a terrible thing, but what if we decide to put a door in this corridor? it has no function, cause we can just side-step it.
So make sure the area surrounding the room+corridor are all solid tiles. If just one tile is not solid, we skip this attempt of a room+corridor cause we might be better off picking another random direction to build in. We then skip this step and try the next direction (not random, but the next direction). If we do this 4 times without getting a new room placed, we end the whole level-creation cause apparently our level is full already.
This is then looped until we create a maximum of X amount of rooms, at which point the game starts!
The above is the basics to create a “room-corridor-room-..” environment. Which is a great start, but we want the world to look a bit more interesting. The keyword is : Recursion!
I decided to have a main path, which is the above described loop. But every corridor+room generation has a random chance of spawning another path from the previous room. So before we go back to the start of the loop, we pick a second random direction, and see if we can generate a corridor+room in that direction. These rooms are not added to the maximum room-count, they are “Extra” and they will always end in a dead-end. This is a great route to hide key’s, switches, secrets, etc.
This makes the worlds bigger and more interesting, but as my friend Simon from Robotality mentioned, all rooms are square..and kinda boring! Great feedback buddy.. ;)
I came up with a fairly simple solution to this problem. First I thought we could create smaller-rooms connected to the room we just created.. interesting idea, but it spawns a bunch of problems for the rest of the code: a room isn’t just the room, it suddenly has other area’s outside it’s borders. It also means we have to do a bunch of new tests to see if the area’s we add these smaller rooms are clear or not, not blocking corridors, etc.
This didn’t work. So a much smarter idea popped in my head: why not just fill up tiles inside the room we just created? By making 2 or 3 tiles solid again alongside the room edges, the room gets a new, and more interesting shape. We still have to make sure it’s not at a spot where the entry-corridor was generated, but that’s a simple check. Obviously not something you want to risk in a small room (3×3) but very much do-able in a 4×4 room and bigger. The result was pretty amazing as you can see on the screenshots.
We now have corners to hide stuff behind, to make it more difficult for the player to grab something and get to a safe spot, etc.
One final thing is making it all graphical. So far the level generator is just all about 1’s and 0’s. To make this visually interesting I created a simple function that checks every tile from top-left to bottom-right in the tile-map, looks at which solid tiles it has surrounding it, and based on that gives it the right graphical wall representation.
This way the level-generation code is very portable to other projects, it’s manageable cause it’s just about 1’s and 0’s, and on the other end, the render-map creation can take any type of tile map and work it’s magic on it depending on what the graphical tile-set requires (variations in walls, floors, etc).
I hope this all helps someone out there to figure out how to do procedural world generation. There are many different routes to take, and in my experience it’s usually best to tackle this from a level-designer point of view. The code really mimics what you would do yourself in a tile-map editor.