Side project progress
Going to jump right to the side project, nothing to report for the engine
I’ve got a tileset I’ve been working on, forgot how fun it is to make a pixel art tileset:
The tileset has grass, dirt, sand, dirt/sand path, and paved path, trees, various buildings, and some mountains. It’s meant to have some tiles be on a layer over the top of the base ground layer, which may make the mapping a little bit complicated, but shouldn’t be too horrid.
I think I’m going for more of a jrpg type thing, or maybe sort of like a roguelike, I’m definitely leaning towards using some proc gen (discussed below) for creating maps/overworld, etc, and I’m thinking turn based will be best.
I’m also not aiming to have different heights/levels in the map, that’s just too complicated art wise and mapping wise, but I’m sure it can be faked with setting up tiles in a certain way.
So I’m planning on using proc gen to generate the map, instead of manually placing tiles, that’s nice because then I can use it to create:
- Replayability, you can play the same game multiple times with different layout, different world, and different experiences
- Larger worlds than I care to make manually, which is great because making large world maps, or lots of small interiors sounds like it could get very tedious, so this way I can make a larger world with more crap to do, without spending too much time on all the individual crap, I just make a tool that makes each type of crap and then another tool to tell the crap where to be crapped out at.
I plan to do at least some of this proc gen, for the world map at least, using Wave Function Collapse. You can see one sample implementation and what it does here but I’m planning to make my own implementation in GDScript (for now, maybe I’ll figure out GDNative later if that’s too slow) so that I can understand it, and potentially tweak it if needed.
The gist of wave function collapse (WFC) as was described in one of the several videos I watched on it, is that basically it’s like solving a Sudoku puzzle, you start with a few pre-chosen tiles, either randomly chosen or given to you, and then you go through all the other slots and you can narrow down the options remaining of what value/tile can go there, until either you get to only one possible tile, or as few as can be, then you just randomly choose on from the options that can be there at that tile, and then you update the rest of the slots with the options remaining.
The way you know what tiles can go next to each other is by defining the possible adjacent tiles to each tile, which if you look at the above github repo, can be done by providing a sample or “training” tilemap/texture, which let’s the algorithm know how/where to put different tiles in relation to each other.
One thing to note about WFC is that it is all based on local connectivity, how each individual tile connects to its neighbors, there is not really any stored/saved data about bigger picture things, which means you can very easily end up trying to make a generator to give you square rooms, but wind up getting non-square rooms, or other similar issues, simply because the WFC algorithm doesn’t really have any data on the inside vs outside of the room. This kind of difficulty can be solved though, by including a sample tilemap that uses a different tile for the interiors of each room, compared to the exteriors, that will keep the room interiors internal to the room and prevent the room from sprawling on.
How is WFC different from Wang Tiles?
Wang tiles are something I’ve done in the past and I think mentioned somewhere upwards on this chain, and I wanted to kind of mention how they seem similar at least in theory to WFC. WFC you define the possible adjacent tiles and try to pick a tile that can go in a spot that is adjacent to the others, based on edge connectivity.
In theory, you could probably combine WFC with Wang tiles and be totally fine, so what’s the difference between what I’m trying to do now and what I did before?
Previously, I did Wang tiles, which the ones I used were basically large blocks that were made up of many smaller tiles/pieces, in this case I’m trying to do WFC on those smaller pieces instead of having to make the tiles, and then manually create rooms/chunks with those tiles, I can just focus on making the little tiles, then piece them all together in an example map, and then let WFC do its thing from there.
Another difference between what I did earlier and what I"m doing now, is that previously I did not propagate any possible options through the map when generating a map, instead I defined the edges of my confined/finite map as being closed off, then randomly chose tiles to fit with that, and make sure that the connectivity of any other already placed tiles fit in with the connectivity of the tile chosen. Is there a reason you need to keep track of and propagate the possible tiles instead of just picking from pre-computed connectivity lists where you group all tiles with specific connectivities together? Not as far as I can tell, in fact I think having to propagate your options through would be slower computation wise, however, this lets you go through and find the tile with the lowest number of possible choices, and fill that in and propagate from there, so hopefully (and this is hopefully, I suspect it’s still possible) there will be fewer instances of the algorithm running itself into a corner where no tile exists that can fill a spot, which happened multiple times when doing Wang tiles, but meant in order to get more generation to work, I had to spend a ton of time manually creating new wang tiles to fill the connectivity combinations that I didn’t create yet, or deal with having a 4 way intersection wedged in there to not block off any paths.
I will say that doing the wang tiles I did before did mean I could just do random walk to make sure you could get from point A to point B by pre-defining the necessary connectivity without defining tiles for those slots yet, which let me create a traversable level from start to finish, but here for an overworld I think it should be possible to not run into too many cases where something would be untraversable, or if anything I can pre-define some parts of the overworld to make sure that things are traversable, but then just proc gen the rest.
Also in theory, with WFC I could make an infinitely generated overworld, and then generate each individual place on the fly. Which, that would be great and all but if I want like a classic story, and maybe some sort of sane leveling system, it may make sense to limit things a little bit to prevent things from getting too insane, although I’m not going to rule out quest and story proc gen just yet, but I think for a better experience for a jrpg type game, some intentional design is important instead of just letting proc gen go completely rampant.
Side quests though, that’s probably where I’ll let proc gen go because if the system generates towns/buildings/whatever, then the system can fill those with stuff, I’m not going to hand make everything that is like that. Proc gen for weapons, tools, items etc will probably also be useful.
Next steps on this project, I think getting the WFC actually working. I’ve rambled on about this, but so far I have a file in Godot that doesn’t do much, it stores out the adjacent tiles from a pre-canned 2d array so it knows how to connect things, but it hasn’t figured out how to use that to create anything just yet, and the propagation of options/narrowing down the options is going to be a little bit intense.
So far I’ve been enjoying making the tileset so that’s good, may make some sort of at least placeholder character for now, or maybe go through and make some items or ui features for later.
After the WFC generates the array of possibilities I think it’ll be a matter of getting it to create the TileMap and then putting that for someone to see, then making it so you can walk around in there, have collision, etc.