7DRL Day 3: Baby Steps Into Content Generation


My pinched neck was recovering quicker than I feared it would, and so I managed to get a better night's sleep. Today would be a good day for 7DRL development. Or, at least it would after dropping off the car at the mechanic. Then I learned I had some mail to pick up, and the pets started begging for attention.  So, as Fate would have it, I ended up starting shortly before noon.

In theory, my actor should be capable of walking around, and the map should render.  However, a lot of refactoring had occurred in a short amount of time, and I wasn't sure anymore.  I wouldn't know until I had a place for them to walk.  I could plop down some tiles manually, but let's skip a step: let's procedurally generate our maps!

I now flip-flopped back to the decision to do tile-by-tile procedural generation, after all.  That's because I realized I could still do chunk-based logic before applying it to tile-by-tile procedural generation.  (I suspect that's how Minecraft does it.)  More importantly, the idea of a Leerie who generates the world around them as they illuminate it doesn't work unless it's like shining a flashlight around and getting different results.

The technical details of how to make tiles that change like this involved four states:

  • Uninitialized - These tiles are far away from the action. So they're not even taking up space on the endless tilemap yet.
  • Initialized but undefined - The player is close enough to these tiles that their data might come in handy but not yet close enough to illuminate them. Especially useful for generating a field of view map.
  • Illuminated and defined - Light falls on the tile.  Before anyone can see it, the generation algorithm generates it. The tile now exists.
  • Dark and defined - These tiles were once illuminated but no longer are. Given that this is a supernatural world that reclaims things in the dark, we simulate this by returning them to an initialized but undefined state.

Now that I figured out the logic behind this, the idea seemed sound and likely to work.  Coding is often that way: once you have figured out how it works, explaining it to the computer is the least of your worries.

Unfortunately, I had an enormous code mistake to reverse.  At some point, I had accidentally renamed the roguelike character class to the same name as a C# interface I created for all things that can be in a roguelike turn-based loop.  I had to go through all the errors generated by this mistake and educate the code on the difference between an RPG-style character and just anything that can act in the roguelike turn-based loop.  That's what I get for thinking my project was too rudimentary to deserve a git repository!  But I found a bit of missing logic along the way, so it wasn't a bad detour.

So it wasn't until two hours of development that I was finally ready to try the procedural generation method I mentioned before. I'm happy to say that the tile generation and degeneration logic worked as planned!

The player normally can't see the question-mark tiles, the "initialized-but-undefined," but I'm clicking a map debug button so you can see tiles being recycled

As for my rendering and movement code, after figuring out that I can't have my tilemap under my UI object because the UI object is moving around, I was able to see a character standing on a tile.

Now for the exciting part: generating a city! I had a pretty good idea of how to go about this. Some of the "chunks" would pull from procedural generation tables related to the roads. Then, the chunks adjacent to streets would be the various city lots (place of business, parks, whatever I have time for).

Though, just imagining it, I could tell that was a waste of my tile-by-tile generation potential. Even if the individual lots between the street had very random content in them, imagine how uninteresting a city would be if it were nothing but blocks perfectly separated by same-sized streets! Nevertheless, figuring out how to get the code infrastructure up to city generation would be necessary, so I decided to go for this simple model first.

I was now past my 8th pomodoro of the day. If I didn't take a break now, I wouldn't have time to unwind for the rest of the day. I made the choice to go for it.  I wanted to have a Licecap screenshot of a generated city block!  Three pomodoros later, I reached my goal:

Tiles getting created and recycled. Note the starting tile recast into a sidewalk.

It wasn't great.  The brick sidewalks were not generating right, preventing the streets from being visually distinct. Undoubtedly a simple logic issue.  But, by this point, I was out of time to work on it. It was time for the daily dogsitting.

I had realized that, since I was using GameObjects to hold my chunk identity models, I might as well populate them with components containing generation logic.  For example, a road might have instructions to place sidewalks on the top and bottom and roads everywhere else.  Then another might explain where the lamp posts go.  The technical overhead is a little top-heavy, but some of that is typical of a WYSIWYG IDE like Unity.

Another thing I realized is that Perlin noise generation is somewhat pointless here.  It isn't random; it consistently generates the same tile at a given coordinate.  However, if Leerie's unique game mechanic is that the world can change when you look away, then Perlin noise was the wrong tool for the job.   I might as well use a random number table to pull the tiles instead.


So much of my current code related to "chunk identities" is probably going into the recycle bin tomorrow. It was a good start, but more of an exploration into what I need to do than anything else.

Good thing I'm only on day 3. I'll need to work fast to get content generation nailed if I plan to add depth to something even more important than the content generation method: the core gameplay loop.  (Though staying up to run this blog entry through Grammarly isn't helping my prospect of sleep.)

Get Leerie

Leave a comment

Log in with itch.io to leave a comment.