Learning procedural generation methods I

Posted by Fabio on Aug. 8, 2012, 11:58 a.m.

Let me preface this post with a few thoughts:

There are many ways to achieve a 'procedurally generated' environment. For example, a method I won't be discussing–arranging pre-created rooms, linked together by random paths. That's a form of procedural generation. So, keep in mind that there are many ways to achieve this effect.

Secondly, I'm not claiming that my way is
the way. There are many other (possibly more efficient) methods of creating the type of generation I'm going to discuss. This is simply the method I use and by no means am I claiming it to be the best or only. I've found this method in particular is best for any type of platform game or top-down game.

I don't want to write this specifically for GameMaker 8 (as these principals can be applied to any and all forms of game making) but please be aware that this is the program I am using. I'm also
not going to provide an example or blocks of code for you to use–that's for you to figure out you lazy fuck. I'm simply going to tell you how to get it done.

Step one: the digger/miner

Don't let the name fool you–we aren't 'carving' or 'digging' out anything. Instead, our digger is going to create 'chunks' for us. The digger should be your first and primary object, outside of a control (which should have some sort of global timer on it for the creation process. You can set this to whatever you'd like after some tinkering). You want this motherfucker to be created smack dab in the center of your room/level.

You want your digger to keep choosing a random direction to move in constantly (in some sort of alarm or step event), as long as there is still time left on our control object's timer. Each time it moves to a new block (let's say 16x16) and there isn't already a chunk there, make one. Simple.

You also want there to be a chance for your digger to create a clone digger (I usually set it at around 6-12%, depending on how big I want the level to be and how fast I want it done). Make sure you have some sort of 'max digger' variable on your control so 10,000 diggers aren't being created. That shit will crash your computer, bitch.

Step two: ending your diggers' movement and creating solids

So once the timer on your control object runs out, you want each digger to stop. Destroy those bitches, get rid of them. Whatever. So now you've got a bunch of background and a bunch of these 'chunks'. Now what?

Well I'll tell you now what, bitch. Next thing we're going to do is create solids. These are going to fill in any necessary background space with a solid block, to give your character some kind of malleable environment to work with–think of it like a fence to the yard we just created.

To do this, you need to perform a check with each chunk (only once our control object's timer is up). We're going to check if there isn't a chunk on my (the chunk we're checking) left side, create a solid there. Then check the right side. Then above, then below. (For GameMaker users, the '!place_meeting(x,y,obj)' code comes in handy here.) So at the end of these checks, each little island in the map and the outside walls of your level will be a solid–nothing else. So there's no wasted processing power on 10,000 solid objects filling unnecessary background space.

Step three: clearing up the clutter (optional)

Now that the digging has stopped and you've created your solids, you're usually going to get a pretty 'messy' looking level. Little solo solids scattered every which way, tiny 1x2, 4x4, or 1x3 blocks floating about. It's going to depend on what you want, but I usually decide to get rid of these stragglers.

To sort of 'clear up' a lot of the singular blocks, all you have to do is perform a check on each solid to see if there is a 'chunk' surrounding each side of it. (Again, 'place_meeting(x,y,obj)' comes in handy here.) If this is true, destroy it. Simple.

Step four: adding pools and waterfalls (optional)

To create little waterfalls, all you have to do is pick a few random solids and perform a check to see if there is a chunk beneath it. If there is, create a waterfall. For my waterfall object, I have it move down (if there is a chunk and not a solid beneath it) and create a tile for water. If it can't go down any further, I perform a check to see if it can move left or right, then it'll keep going in that direction until it gets stopped up by a solid or there is a chunk underneath it–in which case we move down again.

For a pool, I pick the lowest chunk and create a 'source water' block there. Then, every chunk that's let's say, 20 above the source block, becomes water.

After this, we're done with our chunks. You can keep them, or destroy them and place a matching tile in their place to save processing. Doesn't matter much to me.

In conclusion

Remember that this is only one method to get your very own procedurally generated environment. There's so many ways to accomplish this–so now that I've shown you the groundwork, experiment! Trial and error is a huge component in this process.

Here's a few screenshots of some levels I generated using this method: (This is zoomed out, the tiny torches were also added randomly.)

Hope everything was described clearly enough. Thank you for reading.

Comments

JuurianChi 12 years, 4 months ago

Awesome Logic, Fabio.

death 12 years, 4 months ago

It could be nice for a cave but other than that, i'm not sure what it could be used for. Even for a cave it's a bit open. For the most part each room has one large center area with nothing it in and than a few randomized dead ends in each direction. Basically i think it would be an easy level to navigate and wouldn't offer much variety or challenge. If every cave/dungeon in a game used this method they would all end up looking very similar as well.

I think to improve this you could allow for more branches that separate from the center and make them longer, as well as decreasing the center's size or add some walls/blocks scattered in it to give it more of a tight feeling like a cave would have.

However as it is, it could be decent for a Terraria-like game but i don't think it would do well in top-down games.

Fabio 12 years, 4 months ago

Oh! I should have mentioned–you can have a more 'closed, corridor-like' environment created simply by placing a timer on your first digger before it can start making clones. This way, 10 aren't created right away and create that big area in the center. It just depends on what you're going for.

Moikle 12 years, 4 months ago

Instead of doing it in alarm events, do it in a loop, so it all happens in one frame.

I tried this a while back and used a grid, with values assigned to each block, this would be the ID of the type of block used (as there were only 2 types at that time, the only IDs were 1 and 0). The "diggers" are actually all one object which uses a script to do the exact same thing you did there, only it just keeps track of a bunch of x and y values in 2 arrays then sets the block which matches their position from 1 to 0 at the end of every run of the loop.

After all of this happens, all in one step (which does take a bit of time, but probably much faster than the way you had tried) the single generation object spawns a bunch of blocks wherever the grid is a 1.

However, mine tended to make all the levels very round, I think I used too many diggers.

oh and I also deleted all blocks which had all 4 sides touching another block, just because they were not needed as they would never collide with the player anyway and they would just be taking up resources (not much, but oh well, this way you may be able to afford to create HUGE rooms)

Fabio 12 years, 4 months ago

Loops work too! However, they can sometimes lag a computer up significantly (due to everything happening at once). Not a bad idea, though.

death 12 years, 4 months ago

i let a lot of things generate while the player is still in control, i use a loop that only performs 90 times per step, which doesn't seem to lower the FPS at all. Within 1 second you get a bunch of generating done and the player doesn't even have to be aware of it happening!

Fabio 12 years, 4 months ago

I like that idea a lot. With my project using procedural generation, I've been looking for a method of generating a new level while the player can do his thing, to cut down on wait time.

EDIT: Tried this method out and it definitely cut the generation time down by at least 50%. Thanks!

ludamad 12 years, 4 months ago

Just a note, while this is obvious as a good cave design, its also a kickass shape for an island.

Moikle 12 years, 4 months ago

how are you actually doing the movement of the diggers? is there always an equal chance for them to move in any of the 4 directions, or are they moving in one direction, and have a chance for that direction to change.

I think the latter will give you more stretched out, long passageways, whereas the first will give you very round caverns

I also thought of adding a "goal" to some of the diggers. They are slightly more likely to move in the direction of this goal, so you can use it to have a little bit of control over the level for finding an exit point etc.

Rob 12 years, 4 months ago

Quote:
Make sure you have some sort of 'max digger' variable on your control so 10,000 diggers aren't being created. That shit will crash your computer, bitch.

10,000 instances of an object crashing a computer? Oh, Game Maker, you so silly.

Also, you might want to just consider 2D arrays at this point, with all the place_meeting() checks. Those are O(n) complexity (where n is the number of instance in your room, which in a 640x480 (which is way smaller than your rooms) room with 8x8 squares, could be 4,800. So for your case it's probably way higher potentially) whereas array lookup would be O(1) since arrays are random-access and everything's aligned to a grid anyways, so I don't see why you wouldn't just do that. Then after the generation is done you could create the objects (or keep the terrain array-based).