One of my hobby projects is a testament to the Ultima and Elder Scrolls games – a massive open-world, where everything you do impacts the world around you. This has long been promised by commercial games – and it sucks from a gameplay perspective; it’s just not fun. But a very old ASCII-graphics game, Omega, showed a couple of ways of doing it that delivered on that promise while still being “fun” – very fun! That’s what I’m working towards.
It’s a test-bed for the high-performance Entity System I’m writing for Unity (more info here). I’m aiming for “gameplay considerably richer than Skyrim” with “graphics about the level of Oblivion”. To be clear: Oblivion was released almost 10 years ago! To reproduce those graphics today is fairly easy.
Challenge: Believable Cities that evolve over time
One of the things I have yet to see in any FPS RPG is believable cities, and more: cities that actually evolve. The nearest I’ve seen is the beautiful “large towns” of the Assassin’s Creed series (they represent cities, but the distances are way too small).
That doesn’t cover the “evolve” part though – even the AC cities are 100% static and unchanging. Hand-crafted, and because they cannot change (technical design choice) the player is incapable of altering them. You can’t burn down blocks, or build a new bridge. You can’t buy two houses and knock them together.
Hey, RPG industry, you can do a lot better!
This is one of the main new gameplay elements in my design. If all I managed was to take an off-the-shelf-RPG mod and add living, growing cities … I’d probably be content. What does this mean, though?
SubChallenges: “believable” cities?
A few things:
- Cities are never EVER made of grids, unless you live in a few small enclaves of modern America, and got stuck in a timewarp to the 1950s.
- Cities have districts, quarters, etc based on clusters of specialization. Sometimes its based on geography (“the docks”) other times on the people who work there (“the financial district”), other times on the history of the city itself (“old town” – usually the part of a city which is surrounded by medieval stone walls, embedded inside the modern city. For instance, look at London Wall in the City of London. The old wall is still there!)
- When someone important dies: their property gets lost, fought over, split up, goes to various inheritors; the death of a person – or conversely: someone becoming rich or elected – changes the districts of the city
- Cities GROW. London was for many centuries “the city north of the river Thames”. Where’s the Thames now? Smack-bang in the middle; the city grew along one bank, built bridges, and then exploded onto the other side.
Grids and house-positioning
In the space of an hour, I made a quick concept test to show what a city might look like to walk around:
(NB: the odd lighting is because I was testing an idea for a hand-held lantern with dynamic lighting. It kind-of works in Unity, but I’m not 100% happy with it).
Things of note:
- Many dead-ends. This is normal!
- Dead-ends often terminate at a door. Ideally, I believe they should nearly always have a door, or sewer entrance, or well, or … something INNATELY IMMOVABLE at the end – in the real world, an alley without a door is liable to get a wall built on the front and converted into a new house/room, annexed to one of the neighbouring houses.
- In the UK, there are many cities where even the “immovable” things got built over. I’ve lived in houses where a sewer-cover is inside the building because the house was expanded over the top of it. I’ve also seen houses with indoor-wells (for the same reason: originally outdoors, then the courtyard got covered and became a room).
- The city is mostly interconnected odd-shaped courtyards, with a sprinkling of broad streets. In short: if it’s empty, and it’s not full of street traffic, it gets built-on. But not in a careful, squared-off, mathematical way. No, it gets built using whatever materials are to hand, whatever lengths of wood you own, etc, so that the plans of each building and street get more and more crooked over time.
- Hidden alleyways that are hard to spot from a distance, hidden around the corner of a building. Again, in the real world, these are COMMON (how often do you see them in games? Rarely).
- For a modern example, in the city I live now (Brighton), there’s a network of things called “Twittens”. A Twitten is defined as “an alleyway that serves as the only route to the front-door of people’s houses”. i.e. the houses here cannot be reached at all, except via alleyway. They’re cute. People like living in them. They are very quiet even in the center of the city on a busy Saturday night – you can’t fit many drunken people in an alleyway.
Geography, Quarters, evolution, growth, adaptation
This is VERY HARD to simulate using random generation.
It’s been studied obsessively by western culture – the growth of towns, cities, families, dynasties, social-groups, etc is probably more of recorded history than anything except “war”. So we know an awful lot of observed facts about what gives rise to what, when, and how. But how do you tell a computer that?
The brilliant guys at Introversion Software were, for a while, working on a modern-city generator that took into account both “geography beneath/beside the city” and “specialisation within cities”. Here’s a video:
But I need more than good looks: I need to store this stuff programmatically. Because it’s core to my game that I can do three things:
- Implement quests, AI, bonuses, penalties, shop locations, etc that change based on the presence/absence of quarters in the city – and whether or not the player has “discovered” them yet!
- Evolve the city: when the city runs the “grow city” algorithm, I need to preserve some quarters, while replacing others. I need to control that, or it will be too chaotic!
- When the player changes the city (by being very wealthy and building new houses, or blocking-off streets, etc), I need to be able to programmatically, procedurally, detect this and re-calculate the city districts, and have the computer alter the city around the player’s actions
Solution: Generative Grammars
When I read Computer Science at Uni, the lecturers were fond of saying that the two most important things you could learn about programming were “how to make a compiler” and “how to write a new programming language”. It turns out that when you encounter a really HUGE problem, often the quickest way for a human to solve it is to write a new language that makes it easy to describe, and write a new compiler that lets the computer convert that “simple” language into vast amounts of C++ (or whatever) code.
Then you can run a normal compiler on the C++ code, and get your solution.
Using grammars in city-generation theoretically solves a lot of the above problems; in practice, it’s a lot harder than that. Mostly because it’s hard to decide what level of detail to put in your grammar!
The simple, most important thing you need to learn: BNF
If you do any programming at all, go learn Backus-Naur Form; go do it right now!
BNF is a beautifully simple way of representing most of the grammars we encounter in real-world programming. It isn’t just about programming: it’s the OFFICIAL way that all internet protocols are described.
e.g. what’s a valid email address? Can you have an @ symbol? What about spaces? Where can they go? Are you sure? … the answer is a simple BNF grammar that makes it shockingly easy to answer those questions, without any annoying verbose academic jargon. Yay!
Everyone should learn BNF. Anyway, back to the game…
How to decide? ARGH!
I spent a few weeks making mockups of cities, and transformations that would grow / improve / add detail to them. It’s tough, but I had some successes using transforms that accepted “tagged” polygons and output a new set of “tagged” polygons.
But it was quickly clear it would take weeks of work to try an idea and then dismiss it. And since this is a hobby project, that could take me half a year or more per (Failed) idea!
So I needed something smaller.
I decided to do it in 1D first. i.e. a BNF grammar on a simple string. The string reads left-to-right as a profile view of a city.
e.g.: Grass Grass Fields Farm Fields Road Big_Road CityWall GuardHouse Market Houses Houses Castle Houses Merchants Houses CityWall Beach Sea
…this lets me test ideas for GAME DESIGN (what is a “fun” level of detail? how complex do my grammar rules need to be? How many do I need for it to make interesting cities? … how much is “too many” that leads to boring cities?
At first I was visualising it using sentences, but I found that hard to work with as my cities got larger. So … I added a visual representation at bottom of the screen, using graphics I hand-bastardised using Dan’s free game art at Lost Garden.com.
I was having fun with this, but started feeling frustrated there was no point to it, even though I was making lots of mini-cities.
So I realised I could make it into a mini-game. A few hours later, and we have version 0.1 of MiniCity:
http://t-machine.org/files/cityclicker/rogue1citymaker.html (Unity Webplayer, runs in browser)
It SUCKS – yes, I know!
Right now, this is not expected to be fun; I changed the rules a few times, and found some problems I hadn’t forseen. Then I added and removed some stuff to fix them, and … ran out of free time. I hit the publish button because I think this is a very interesting idea. Don’t judge this as a game! It’s a design-tool, not a game.
BUT … on future evenings/weekends, this is something I can add to quite quickly. I’ve already started working on the 2D version: top down view of a city, using arbitrary-shaped polygons instead of bitmap tiles.
How did I do it?
In case you’re interested, I left the actual BNF transformations listed on the buttons. It’s not written in BNF syntax (sorry!) but if you’ve looked at BNF, you should easily see what’s going on.
NB: I started by writing (on whiteboard) the BNF I wanted. Then I implemented that, one rule at a time.
The rules themselves are using Regular Expressions (C#’s “Regex” class), because I spotted that this was a super-fast way for me to get somethign up and running. It will be absolutely useless for the 2D version – but Regexp’s are specifically designed to work brilliantly on 1D input (strings).
You should be able to understand what each rule does, and why it works, from looking at the button names.
This is part of my work on Entity Systems in Unity, and a living, breathing RPG.
If you’re interested, sign up here (I’ve sent only 1 email per year so far, but hope to start sending a few more soon. Maybe 3 or 4 a year!).
4 replies on “Writing a #unity3d mini-game … to help you design a AAA game”
Great article, very interesting. I will try your city maker as soon as I get hom from work :D
I promise you’ll get bored quickly :).
A few more versions, and it should becmoe good.
Hey, I’m here after seeing some of your posts on Reddit about the civ clone you’ve got going.
While at first the rules like “VVR -> T (Grow town on top of river)” appeared to be permanently discarding meaningful historical / geographical data, I almost immediately jumped to seeing it (combined with an underlying representation allowing for stacked strings) as a perfect way to embed priors into the current state of a city element, allowing them to be expressed in the resulting visuals and game data.
I wonder, though, how you would extend a system like this one beyond initial world building? Is it even intended to? With a representation this coarse, there’s no way (that my puny human brain can think up) that the resulting visuals and game data could be totally deterministic without things become bland and repetitive very quickly. So how do you tackle the problem of a grammar rule firing and altering the city’s string when the player has already experienced the city as a representation of said string in its previous / original state?
I’m curious to know how you might come at this.
That’s the idea, yes.
If I could find a good generalization to 3D, I would then supplement the grammar with “shuffling” rules that e.g. make:
VVRVVVR go to RVVVVVR
…or do more cosmetic things like having V1 V2 V3 V4 that are all “V” but look different, and are persisted.