[Spoilers] How The Game Works
Thanks for playing my 2018 #procjam entry! This is a short post explaining the tech and implementation of Nothing Beside Remains. It's designed to be read after playing the game, and completely spoils how most of the game works. Only read on if you want to know exactly how the game was made (and possibly have your ideas ruined!)
Simulating The World
The plan for this game was to simulate the life of a village in such a way that it always led to collapse, then age the village, then place the player in the village a long time later. Since I started this on Thursday evening, and didn't want to spend too many hours on this prototype, I pretty rapidly had to cut down on my original plan!
In the end, world generation had two aspects: ecosystem and society. Society was a model for what the people had, it was super-basic: population (also recording working-age vs old/young), crops, and then some additional features that didn't affect the world generation but did affect world rendering (we'll get onto those later). Ecosystem was a bit more complex (albeit not much). It recorded:
- Temperature (along with min, max, and a maximum delta-per-tick)
- Air Pressure (along with min, max, and a maximum delta-per-tick)
- Hostile Fauna (along with min, max, and a maximum delta-per-tick) expressed as a percentage
- Health (starting at 100, representing the general stability and health of the local ecosystem for supporting human life)
This began as an ambitious attempt to model more specific ecosystem features, but I ended up cutting it down massively, as you'll see. So we then run the world until it collapses. Each tick:
- Temperature, Air Pressure and Hostile Fauna change randomly, affected by the delta-per-tick to make sure they don't change too much in one go.
- We roll to see if the ecosystem takes health damage. This is more likely at high temperatures and with high air pressure, and the amount of damage also scales up. The ecosystem can never recover from damage.
- If the ecosystem health reaches zero, society collapses.
- The society then loses some members based on the hostile fauna presence.
- If the population is zero, society collapses.
- If the society's crops are at enough surplus, new members are born.
- The crops then either grow or shrink as a function of the temperature and air pressure.
- The crops shrink a small amount based on the population size (consumption).
- If the crops reach zero stock, society collapses.
The exact balance of these parameters were tweaked until the three collapses were happening with roughly equal frequency. These are the three 'endings' you can hunt for in the game - only one will occur in each village.
Rendering The World
At this point we know how society ended, and we know the current state of the ecosystem. We also fix the ecosystem at this point (we could keep running it but there's not much functional difference as the player has no way to interpret change over time - only the final state). To render the world, we're going to iteratively place down things in the village space. Ideally we'd rather render the village during simulation, but there just wasn't time to do this, so it's all done post-hoc which means we can't actually intuit stuff from structure, but that's future work.
The world is a 100 x 100 tile grid, split into 10 x 10 blocks. We begin by scattering a few lakes across the map. The lakes can be assigned anything up to a 2 x 2 block unit, but the actual size of the lake within is based on the ecosystem state at the end. Smaller water sources suggest ecosystem damage, or possible collapse.
After the lakes, we begin with the few things that are in every village generated by the game:
- The Statue - a nod to the Ozymandias poem, and where the player starts the game. We generate slight variants on the pedestal text from the poem, and scatter some parts of the statue around nearby.
- The Church - originally, villages were going to have multiple 'focal' buildings, but we ended up with just one. The church is a 1x2 block building that contains pews and altars.
These elements also exhibit conditional generation which we use throughout the game. The first phases uses Tracery to generate some descriptions, like "A small altar. A ceramic jug sits on top of it." But some of the Tracery patterns contain wildcard references to the society, like "The side of the altar displays an engraving of @DREAMENGRAVING@." Once the text comes out of Tracery, we pass it to the Society object which fills in these wildcards with relevant features. Every society has a number they hold sacred, for instance, and it crops up throughout the game in small descriptions.
DREAMENGRAVING is the most special tag in the game, because it actually reveals information about how the society ended. If the population dwindled due to wildlife, the engravings you find will be of cities with high walls surrounded by monsters. If crops decayed, they'll be of lush fields. I wanted a lot more of these contextual clues to be in the game, but I sadly ran out of time. The engravings remain one of the most crucial clues to the fate of a village.
After this we go through a few cycles of generating the village. First, we beat a path from the church to the nearest water source. Then, along that path, we place houses with a certain random chance. We'll discuss housing implementation in a second. After that, we randomly create some paths from gaps further out into empty space, then add more houses along these paths (with a chance that some of these houses will be barns - more later). Then we do this one final time, this time with the houses either being regular houses or fields.
Houses, barns and fields are all structurally the same: we reserve a block, and for barns and houses we lay out some walls, leaving doorway space for the road. Fields are similar but we use fences instead. This outer structure has a chance to be decayed - depending on the temperature and air pressure (as an approximation for weather intensity) the bricks may be one or even two tiles away from where they should be, or simply not present at all. Fences can be destroyed.
Inside these spaces, we roll against a content table. Most of this content has a fixed chance of appearing, but some have their chance modified by the state of the world and the reason for its collapse. For example, the presence of toys in houses scales with the number of people alive at the end of the simulation. In barns, the presence of predator skeletons can indicate a high level of hostility from the natural world. In fields, the ratio of weeds to surviving crops can also reveal something about the ecosystem health. There aren't as many of these clues as I wanted, and the placement of these building types is quite haphazard, but they're definitely there in proof-of-concept form.
Obviously there was a lot I could've done here, but I have a lot I need to be getting on with so I've left it in a complete but small state. I do have plans to build a game in the future in a similar vein - but a lot of its core systems, and its theme, are totally different. I imagine you won't hear much on that front for some time, but you're welcome to keep in touch on Twitter!
If you have any specific questions, you can ask them in the comments and I'll try and respond. Also, if you're a fan of procedural generation, check out my research papers, game development and work on creative AI!
Leave a comment
Log in with itch.io to leave a comment.