alchementalist alchementalist devlog personal blog

Alchementalog #4: The Essence of Flavour (Part 2)

A look at the process of adding flavour to a randomly generated map…

All Alchementalogs

Table of Contents

Umami, Ooh Mama

In the last devlog (Alchementalog #4: Delving the Deeps (Part 1)) we went through the process of generating the basic rooms for the Ember-mage mine that the Alchementalist will explore. When we ended, we had a nice series of linked rooms, but those rooms were rather plain. In this devlog, we’ll go over the process of adding flavour to the rooms.

Wait a minute, what do I mean by flavour? Well, by flavour I mean non-interactable art that adds backstory and general visual interest to the map. The things that don’t add anything mechanically to a game, but make it more visually alive and can subtly add to the existing backstory.

To begin with let’s outline the problem I’m facing. If you remember the main problem from the last devlog, it was that our map is being generated by cellular automata and this doesn’t naturally fall into some structure that would make sense as a series of “rooms”. Instead, we simply have cells that are either wall or floor. We managed to get around this by harnessing the power of flood fills and lists (or arrays, really). The strength of this approach was that we could end up with some really gnarly room shapes that didn’t need to be predefined…However, that strength is also a weakness when it comes to adding flavour in a sensible way.

If we were doing Spelunky style generation, we would be able to predefine the placement of all of the interesting features and simply randomly pick from the valid ones come generation time for each room type. But because everything about our rooms is being generated procedurally, we don’t have predefined room structures to draw from. How do we fix this?

As an aside before we really dive in, here’s a few of the flavour images as they currently are, btw:

We’ve got a few tables, some chairs, some mining equipment and a few random “detail” images. They’re all 16×16 so they fit on a single tile (although we could easily make images that extend across multiple tiles).

As it is right now, the thing I’ve been doing is rolling a random number on each floor tile and if it’s below a certain threshold, adding a random flavour image to the tile. You can see the outcome of this in some of the old screenshots:

There’s a broken table and a few broken chairs. But they don’t really make sense in their placing. What I would like to be able to do is build up some structures and have them “search” for a place in the map that makes sense for them to go. Let’s have a think about how to do that.

Crafty Crafting

The first step is to build a “Flavour Editor” of sorts so that I can start creating the flavour structures. I’ve spent the last day or so making a pretty simple editor and here’s the outcome (sped up a little for brevity’s sake):

Whenever I have little side projects like this, I usually try to teach myself something during the process, whether that’s a particular technique when working with surfaces, something shader related, or something mathy.

In this case, I wanted to work on converting back and forth between 1D and 2D arrays. Usually when I work with maps I implement a 2D array of the format map[x][y]. I have done very small scale work with implementing 1D arrays and then translating them into 2D arrays, but not much. So here my goal was to build the flavour maps as a 1D array and then convert them to 2D arrays when saving, and then de-convert them back into 1D arrays upon loading back into the Flavour Editor for editing. There’s not really a technical point to it, more just practice for myself. It’s always good to be building and honing skills.

So here we have a 1D array with the mouse position being converted into the 1D position for the array like this: pos = mouse_x+(mouse_y*map_width);. Then when I want to figure out the 2D coordinates from a position in the 1D array, I use this formula (let’s assume it’s position 100): x = 100 mod map_width; y = 100 div map_width;. It’s pretty simple, but there were some complications along the way.

For instance, I wanted to compress the maps to their smallest possible size, so any unused space is removed. This meant deleting every position in the array before the first flavour item is found, and deleting every position after the last flavour item is found (well, technically I’m actually saving a rectangular bounds, so it’s possible for the first few entries in this new map to be empty or the last few entries to be empty, it being rectangular just makes handling it a little easier later on when reading it into the game map).

This compression makes things a tiny bit harder when saving, as I have to find the min and max positions, alongside doing a little more maths than I posted above to make sure everything is aligned. For example, in the uncompressed flavour map when I am simply placing flavour items, the map_width is always going to be room_width div CELLSIZE, but once I compress each “row” so it’s width is only as long as the rightmost flavour item, it’s going to incorrectly reposition all the flavour items due to the map_width being different from the one they were placed with (remember the formula for finding the x and y positions from the single 1D array position requires map_width so any change to this changes the positions). I can get around this with a little more maths; basically taking the min and max limits for the x and y positions of the flavour items and using those to determine the length along the x and y axes that each row/column should be when compressed and then looping along those lengths using the start_x and start_y positions as boundaries to read from the 1D array:

var len_x = end_x-start_x;
var len_y = end_y-start_y;
for (var xx=0;xx<len_x;xx++) {
	for (var yy=0;yy<len_y;yy++) {
		var i = (start_x+xx)+((start_y+yy)*maps[j].map_width);
		_map[xx][yy] = maps[j].map[i];

start_x, start_y, end_x and end_y here all correlate to the first flavour item coordinates and the last flavour item coordinates on the 1D grid (with padding to make sure the final shape is rectangular).

Not too hard, but maths ain’t my strong suit so this type of stuff trips me up pretty regularly.

In any case, it was fun to flex some rusty skills when it came to these types of conversions and I end up with a nice rectangular 2D array that holds values that correspond to each type of flavour item. In the gif where I am placing them, it looks as though each item type is identical (all the desks are the same, all the chairs are the same, etc), but when I read these flavour maps into the actual world map for Alchementalist, it’ll have a conversion array which gives a range of numbers that correspond to different sub-images of whatever “type” the thing is. So for the desks, it only ever shows me sub-image 0 when I’m building the flavour, but when it reads the flavour into the map, it’ll randomly pick in the integer range from sub-image 0 to sub-image 5 (as we can see in the picture of the actual flavour items, there are 6 desk images: sub-images 0 to 5).

Places To Go, Flavours To See

Ok cool, I’ve got a flavour editor that I can use to build as many different “room” layouts as possible. That gets saved to a file, and I can include the file in the main game and read the flavour layouts there. So how do I do that?

Well, as per the previous devlog, I’ve got a list of the rooms the map generates and each of the entries in the rooms list also contains a list of the cells that room contains. So all I need to do is loop over the rooms, and check the cells in each room against the cells in the flavour maps. If there’s a section of cells in the room where the walls align with the walls of the flavour map (we consider both the floor tiles and the flavour tiles in the flavour map as empty for this purpose), we can choose to slot in the flavour items from the flavour map (and we then mark the cells that the flavour map covered as off-limits for future flavour maps so we don’t get overlapping things). Whether a valid flavour map gets slotted in or not is also random, as this will add yet more “beneficial” randomness to the generator (beneficial here meaning something that can’t detract from gameplay, but yields a more diverse and interesting layout across multiple playthroughs).

However, having done all this, I realised there was a slight problem. What if I wanted to be able to choose between multiple flavour layouts for the same section of map? As a visual example, something like these two:

They both fit the same wall sections, but right now, I have no way of telling the system “if you find these wall sections in the map, pick between these two types of flavour map.” So rewriting in is order.

To make a long story short, I went through and changed the Flavour Editor so that it had two sections, “Flavour Types” which is the basic “wall groups” to look for and then “Variants” which are the different flavour layouts to choose from when that wall group is encountered. On the bright side, this let me clean things up a little bit, so most of the code I posted above isn’t accurate to the state of the code now but the essence is the same, just a few layers deeper in arrays. Here’s the fancy shmancy new Flavour Editor, with the flavour types and variants added (really sped up):

Walls carry across through the different variants, I can manually set the bounds (which is more useful than auto compressing them), and a new flavour type lets me build a new wall structure to base variants around.

Finally everything’s coming up Milhouse! I can create basic wall layouts for the game to look for, if it finds one, it decides whether to fill it or not and if it does decide to fill it, it can have multiple flavour layouts to pick from to fill it with. Awesome. Let’s have a look at the whole cohesive product:

First, I have two variants of a wall structure in my flavour creator, then we switch over to the main game and see where it’s placed them. You’ll notice that in the flavour creator, all the flavour items look the same whereas when they are placed in-game, the game picks from the range of similar items to fill (so some desks have paper, others don’t, etc). Also, there are sections in the map that fit the basic wall structure for the flavour layout, yet don’t have any flavour on them because the game rolled a number and decided that that section wouldn’t be filled.

Now that’s a spicy meatball! As long as I can think of cool shapes to fill in interesting ways, I can populate the map with as much flavour as I like. Stay classy San Diego!

By RefresherTowel

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

Leave a Reply

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

You are commenting using your 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