Categories
personal blog tutorial

Generating Maps (Bonus Plug and Play Map Generation Code Inside)

While I’ve been working on the Workshop update to Spaceslingers, I’ve been doing some procedural generation work on the side. Proc-gen is one of my favourite things to play around with, it’s always super cool to see what you can make the computer create. So, as is my wont, I’ve been making map generators.

Contents

THE “CPU” MAP

THE “GRID” MAP

THE “JIGGLE” MAP

THE “DRUNKARDS WALK NOISE” MAP (CODE INCLUDED)


THE “CPU” MAP

Here’s one that uses a branching structure to create parent and child nodes that flare out. First, a center node is placed, then the number of child nodes is randomly selected. It then chooses a random angle from all possible “45 degree” angles, places a new child node, marks the parent in the child node and the child in the parent node and continues until it has done so for all the children it wants. Then the cycle repeats again, this time for all the child nodes. Once a certain number of nodes has been placed, it cancels the generation.

When moused over, the current node turns yellow, the child nodes turn orange (with yellow connector lines) and the parent nodes turn aqua (with blue connecting lines)

However, I was having trouble maintaining good distances between children nodes of different parents (as you can see in the gif, there’s a lot of overlap going on) and since the generation wasn’t really looking like what I wanted anyway, I abandoned it. I think it’s quite a pretty generator, if a little crowded, and it reminds me of some sort of circuit board or electronics structure. Might be good for randomly generated skill trees.


THE “GRID” MAP

My next experiment was based around a grid structure. Basically, create a grid, select a circular region of it and mark each cell in the circle as a potential village. Then I loop through and randomly remove some of the potential villages. Once that’s done, I create the village struct and connect each one to whatever cells around are marked as a potential or existing village. When the village structs are created, they choose a random position within a range of the grid cell size (I think in this picture, the cell sizes are 64×64 px). Finally, I do one more loop and remove any villages that aren’t accessible from the starting point (which is the village in the direct center of the map).

I actually really like this one, it’s simple, it works effectively and it creates a structure that is basically exactly what I was thinking of when I started playing around with the maps.


THE “JIGGLE” MAP

But of course, I couldn’t stop after finding what I wanted, there were still so many possibilities left. The next map generation I did was what I’m calling a basic “jiggle” map. It involves randomly placing nodes around the map, and then jiggling them apart if they are too close to each other. Once they are all a minimum distance apart, it loops through each node and selects 4 (I think?) nodes around the current node being worked with and marks them as “accessible” from that node. The roads are generated very simply by just walking from “cell” to “cell” from each node to each other accessible node until both x and y line up.

If I were to actually work on this generator properly, I’d probably end up using an actual pathfinding algorithm for the road connectors, and choose the “accessible” nodes in a smarter way than simply nearest 4. Also, I didn’t bother writing a destructor for nodes that can’t be reached, so there’s some sections that don’t have connecting roads between them sometimes.


THE “DRUNKARDS WALK NOISE” MAP

Get the script for this generator here on my pastebin.

Finally, there’s what I’m calling Towel Stomping, or, more accurately, I guess it’s like a Drunkards Walk combined with some techniques used in true noise generation (like a magnitude for each generation of walkers so they influence the map less as the details they are altering get finer). It generates faster than Perlin noise in pure GML (it may be faster than Perlin noise generated in a shader and then read into a buffer as well, depending on circumstance). Basically, you input how many walkers you want, a “granularity” which is how big you want the first generation of walkers to be, the lifespan of the walkers and finally whether you want the walkers to alter a circular region or a square region, and then the generator is off to the races. Here’s a simplified version:

The generator basically takes the input values you give it, works out the best ratio of those values for each generation of walkers (if you set granularity to something large like 20 and also have a large number of walkers being created, it tends to smooth out the noise too much, so the number of walkers is altered by the size of the granularity, as an example), and then creates generation upon generation of walkers, with each generation halving the granularity and also changing the amount of influence that generation has on the noise.

The walkers themselves walk around the cells of the map and add to the current value, which means that nearby cells end up being semi-related numbers (as all good “heightmap” style generation should do). After all the walkers die (which occurs after the number of steps they have taken is equal to the lifespan you give them) it creates a new generation of walkers with the appropriately altered values and continues on until granularity has gotten lower than one (once granularity hits one the walkers are effecting only one cell at a time, so going lower is useless). Then I simply loop over the map and normalise the values to between 0 and 1.

The titular image is made with “Towel Stomping”. Cutoff values are being used to determine whether a cell is plain ground, forest, mountain or water.

The gif above isn’t really indicative of how the generation works, as normally you would experiment with the input values (which you can see in the top left corner) until you find one you like and then use those values every time to generate the new maps, but here I’m randomly picking those values each time a map is generated, so it’s more a display of the range of map types you can have with all the settings, rather than the range of maps you can have within a specific setting.

It was a fun attempt at creating a custom noise generator (though I have been told that this sort of generation has been done before, which doesn’t surprise me at all tbh). Might be useful as a quick and easy generator for anyone, rather than having to mess with Perlin noise or anything complex like that. So have a look at my pastebin to get the code for it (it’s in the scripting language called GML for the program GMS2, and requires v2.3 to function).

By RefresherTowel

I'm a solo indie game developer, based in Innisfail, Australia.

3 replies on “Generating Maps (Bonus Plug and Play Map Generation Code Inside)”

I do get an error on line 121.

___________________________________________
############################################################################################
ERROR in
action number 1
of Create Event
for object Object1:

global variable name ‘map_height’ index (100008) not set before reading it.
at gml_Script_Map (line 121) – static map_grid = ds_grid_create(global.map_width,global.map_height);
############################################################################################
gml_Script_Map (line 121)
gml_Object_Object1_Create_0 (line 3)

Using latest GM studio runtime 2.3..2.420 and IDE 2.3.2.556
Thank you for your time.

Like

Good catch! I believe GMS updated the way static variables works in one of the recent releases, which means that all static variables are declared before any other variables in the game, which means that map_grid is trying to grab global.map_width and global.map_height before they are declared (or I’m just crazy and this bug was always there and I missed it).

The fix is to simply remove static from before map_grid (we’ll replace it with a global) so this line:
static map_grid = ds_grid_create(global.map_width,global.map_height);

Becomes this:
map_grid = ds_grid_create(global.map_width,global.map_height);

It doesn’t really change anything about the game if you do this, the only difference is that whatever instance is calling the new Map(); constructor will inherit an instance variable called map_grid. The rest of the code remains unchanged and works the same.

(I’ve updated the pastebin to reflect the changes)

Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s