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.
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.
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
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 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).