Hey and welcome to this new devlog where we will go on a [b]secret mission to sneak into the map generator[/b] and see how it works.
[img]{STEAM_CLAN_IMAGE}/43767984/3c1823cccf0bc934ebe4b002a07234a2f6515fb4.png[/img]
In Aethermancer we use procedually generated maps for our overworld, that are based on different biomes. If you want to learn more about what our maps in general look like before we get started, feel free to check out this tour of Terastae we did earlier this year. I will keep this non-programmer-friendly since we'll mostly be [b]talking about the logic and system behind all of our maps[/b] - so let's get going!
[img]{STEAM_CLAN_IMAGE}/43767984/3647d3f111561d486a9dd7591f88c0b739dce60b.png[/img]
Depending on how you want to generate the map, there are multiple routes you can go. In general there are two types I would distinguish: You can use [b]noise[/b] as the base (Minecraft does this) or you can use [b]rooms[/b] (most roguelike/roguelites use this). Since we need quite a bit of control over what we generate and how much space we need, we're using a room-based system. However room-based does not mean that you will be trapped in one section of the map, it’s more used as a base for the generation and will still allow you to roam around the map and explore.
[img]{STEAM_CLAN_IMAGE}/43767984/d08980de37eaaa8aa74ff4bfb5fae0b1e3a4bf34.png[/img]
So the first thing we need is a [b]general layout [/b]for our map that is based on rooms. Depending on what exactly you want those can look quite different. Below is what one of our maps would look like, which we will use as an example for now. All of our areas have four points at which they can connect: North, East, South, West. Depending on which points are connected, we end up with [b]different shapes for our areas[/b]: O-area (1 connection), I-area (2 connections, which are counterparts), L-area (2 connections, which are not counterparts), T-area (3 connections) and X-area (4 connections).
[img]{STEAM_CLAN_IMAGE}/43767984/117a95ee2f6f2208de7488a29580eb369dfc8d2f.png[/img]
Okay, now we have the basic structure for our maps but if you run across that map, the shapes are a bit ... meh. So instead of using the basic shape of each room, we use something called [b]area prefabs[/b]. These contain predefined shapes (and a bunch of other things) which we use to define which part of our room is accessible and inaccessible. We are using a mix of these area prefabs that are then procedurally filled as well as completely hand-crafted, premade areas. For the sake of this devlog we are ignoring premade areas.
[img]{STEAM_CLAN_IMAGE}/43767984/d7f5c9354963dafa95f3d1b24e3ba860366a6d48.png[/img]
But there's one thing about these prefabs that is restricting them. If we have a room that connects North & South we can't use a prefab that connects East & West right? So we would need two different ones. If we now look at O-, L- and T-areas, we would also need 4 different ones just to cover every direction once. That seems like a lot of work so why don't we use a trick and do some sneaky magic to get rid of our problem? The solution we're looking for is rotation. By turning our area prefab we can make it fit for all rooms of the according type. Now we (in theory) only need one are prefab per room type, but if we use more than that it adds even more variety to our map. We use quite a few currently and there's always the option to add more.
Now we have a map that has a defined accessible area. Which is cool but ... we actually need to decide what goes where and how we want to fill the whole map? If we just filled it with tiles depending on if it's accessible/inaccessible that would be really boring ... and we don't do boring at this company so let's make this map pretty! For that we're using [b]subbiomes[/b] which define how a room gets filled in terms of [b]terrain and props[/b]. So depending on what subbiome and what area prefab we use, we end up with different rooms, which is pretty neat. Now that the accessible part looks cool, let's get to work on the inaccessible part!
[img]{STEAM_CLAN_IMAGE}/43767984/f49f63e608b121f72958f4ae0187cb071015c0c9.png[/img]
So the first thing we'll need to do is determine [b]how much of the inaccessible part[/b] of the map we actually need. There's little use in generating a bunch of things on the other side of the map if no one is ever going to see it, because they can't get there. So we're simply going to [b]extend our current outer border[/b] depending on how much space you can usually see on the camera.
Let's figure out what cool stuff to place and where we want to put it. We could come up with a general structure of what we want the inaccessible area to look like and place that everywhere but again ... that's kinda lame. So let's [b]divide the outer area into chunks[/b], we're using the [b]lazy flood fill[/b] for that. The nerds that want to learn more about how it's done can check out [url=https://www.youtube.com/watch?v=YS0MTrjxGbM&t=254s]this video[/url], for everyone else: We now simply have multiple smaller areas we can work with instead of one large one.
[img]{STEAM_CLAN_IMAGE}/43767984/ce3000e096339b26f208b21b9599fb21501f76c0.png[/img]
Previously I mentioned that the subbiomes have more information stored in them, among other things they also store [b]what type of inaccessible subbiome can be placed next to them[/b]. This enables us to place inaccessible subbiomes with[b] more intent[/b] so that things feel well placed instead of misplaced. Each biome can have its own type of border (e.g. water or fences) as well as a collection of different props that get placed in the area.
[img]{STEAM_CLAN_IMAGE}/43767984/6502113a08cc2014ffbd09643f7dc5e2986b94ae.png[/img]
Okay now our map look quite nice and like a charming place to fight monsters. Now that's all, right? Well you know, it's a pretty good map but ... isn't it ... kind of ... boring ... in some places? Don't burn me just yet because we have more exciting stuff to add to the map! What if we wanted to aim higher and ... [b]add heights[/b]?
For that we'll use a [b]simplified version of the Wave Function Collapse[/b]. You can use Wave Function Collapse for a bunch of things but we will mostly use it to determine what kind of height we need where, which is a lot simpler than other cases where you can use it.
In our case we have the following [b]basic rules:[/b]
[list]
[*] Each area has [b]four sides[/b] (North, East, South, West)
[*] Each side can either be [b]connected or not connected[/b] to another area
[*] Each side can either be a [b]height or not a height[/b]
[*] If two areas are connected, the connecting sides have the[b] same properties[/b] (since they are kind of one thing)
[/list]
Keeping these rules in mind, we can determine for each connecting side whether it is a height or not. Any non-connecting side could be either, so for them we randomly decide whether they are a height.
[img]{STEAM_CLAN_IMAGE}/43767984/f8c54cf01c7c016b6ab2ac87bbf534d1f275e478.png[/img]
Okay so now we know which side is what but ... what now? Well, the good thing is each of our area prefabs also has a [b]height prefab for each combination[/b], so we can use them to determine what positions end up being heights (aka what the actual heights look like).
This system makes sure that all combinations make sense and while also giving us a lot of [b]freedom of how we want each height[/b] to look like. As long as the sides connect with the right heights, we can do all sorts of weird and cool stuff with heights [b]without risking the map being unuseable[/b] (in most cases, there are still some restrictions as there are with almost everything).
[img]{STEAM_CLAN_IMAGE}/43767984/cefc10bad1d562ef723842eb416133a6d61943d2.png[/img]
[h2]I hope this devlog gave you a glimpse of what is going on behind the map generation of Aethermancer! We do have a bunch of other things that are going on in the map generation, but this devlog is already quite full with information so we'll keep that for another time.[/h2]
[img]{STEAM_CLAN_IMAGE}/43767984/538cf245173af986a0f79f9bd1576e3491144861.png[/img]