「Dev Blog」Procedural Buildings in Despina

This is the dev blog for Despina — a procedural city planning game I’m working on.

 

I’ve been brainstorming on what’s the best way to achieve different styles of buildings for a procedural city building game.

The first attempt was simple. I compartmentalized each part of the building, and set  each component to randomize between an array. Now each building has a random door, body shape, roof or windmill. Of course eventually there could be more styles, details, slots for windows and whatnot.

I stumbled upon a couple of articles about an algorithm called Wave Function Collapse, and it inspired me a lot. What would these buildings look like if I provide simple geometries and rules on their compositions? How should we write rules and use functions in Unity such as collision to simplify the process?

I started by using only 3 very basic symmetrical building blocks, and each has available ‘faces’ that allow the next block to attach. In this case, the cube has 4 sides, triangle has 3 and the semi-cylinder has 1.

So in Unity, each ‘block’ has a UnitScript attached to it, and each UnitScript documents how many available faces it has, and many other parameters. For example, the cube has 4 available faces horizontally, and 2 faces vertically. Here the 4 horizontal faces are marked as va, vb, vc, vd (a mistake earlier but doesn’t matter).

And each Unit has an Initial Face: va. So va face is the one that’s used to attach to an existing block if a building is created. The va faces are shown as the blue line in this picture:

So when a building is created, a new block is created with the building, kind of the ‘center block’ where all the other block spawns from. And the center block have to be either a cube or a triangle, not cylinder. Here is the CreateBuilding function:

When a building is created, several things happen:

  • Building created
  • Center block created
  • Center block initiated (UnitStart)
  • All available points of center block get added to total list of the building

While the Building gameObject is created, the BuildingScript starts to run its CreateFunction. There is a ‘CreateBlock’ function that gets called in Update until the block amount reaches the maximum. As shown below, ‘vp’ in the BuildingScript is a list of points (faces) that are available to connect to new blocks. Keeping a list of all available faces is very important.

So in CreateBlock function, this is where most of the procedural gets done. When a new block gets attached, here are a list of things that need to happen:

-Step 1. Check what’s the connected block’s property, is it a cube or a triangle? And get the rotation of new block accordingly. Ideally there would be a function that calculates the normal direction of the connected face, but that’s some crazy math that I decided to not spend too much time on. Please feel free to throw suggestions if you have any 😉

-Step 2. Decide which block is to be created (square, triangle or cylinder?)

-Step 3. Get the position of the connected face, and set position of the new block. Also set the rotation got from before.

A couple of things that need to be explained here:

First, if (blockdp.HasCollapsed) then return.‘blockdp’ is the script attached to the block that is being created. And HasCollapsed is a boolean that gets turned on if this block is colliding with an existing block.

Each block has a trigger object inside them that are slightly tinier, and when those trigger collide with an existing block, a class on the block’s UnitScript is called to collapse one of them, ant that’s the ‘CollapseBlock’ function.

So when 2 triggers collide with each other, how do you decide which one to deleted?

In the first if statement, if the colliding unit (OtherUnit) hasn’t collapsed yet, and my block’s linked units (which is a list) is 0, means that my block isn’t linked to any other objects (means that this block is fresh) If all that requirement gets fulfilled, then collapse this block. (The newer block collapse first).

If a block that already has connected faces gets collapsed aka deleted, something like this would happen:

The yellow triangle is there by itself because the supposedly connected cube was deleted right after it connected the triangle to the building, that’s why only a block with no other new connections can be collapsed.

Also in the second if statement, this block needs to be initiated, because initiating a block means that all the points (available faces) are stored in the BuildingScript, and right here when we’re collapsing a block, all the available faces of this block needs to be removed form the total available points of the building.

Remember the CreateBlock function is called referring to all the available faces left. So if the available faces don’t get added or deleted in the correct order, the whole function would fail.

That’s why, in the CollapseBlock function, we need to ensure that the block already has been ‘initiated’. And in the CreateBlock function where we initiate the block, we need to make sure that the block hasn’t ‘Collapsed’.

So without the collapse function, something like this would happen:

Now, after setting up the collapse function, I get something like this:

Beautiful isn’t it??

And if you don’t set a limit to the amount of blocks, it’ll go on forever —— until Unity decides to quit.

This is the first part of this subject, more coming soon!