[Source Code]

Introduction

A couple of weeks ago, at the Exile gamedev retreat, I worked on some more threading work for Bad North and found some nice generalised approaches to handling background tasks that take a long time (in the region of several seconds).

In some ways this is a continuation on my previous post about multithreading in Unity, but it’s also covering a much simpler usage cases. Previously I was looking at syncing background worker threads with the main thread every update, but now I’m looking to kick off background threads and let them run over many frames, until they are done. Along the way I stumbled across some nice ways to pause, resume and abort threads, by using Enumerators as the thread implementation.

Application Overview

Probably the biggest issues with the level generation tech for Bad North is that the algorithm can take a while to find solutions, due to the following:

  1. A very large number of comparisons between mesh pieces need to be done.
  2. The algorithm can find itself at a “dead-end” where it’s no longer possible to resolve.
  3. The generated levels can be unsuitable for play (they do not score well enough against certain criteria).

Up until recently, we’ve been mining for seeds offline, and then generating the levels at run-time with known good seeds in-game. This counters point 2 and 3 as failures and restarts are not needed with good seeds. However, generation can still take a while, putting a noticeable delay between selecting a level and being able to play it. We also would much rather ship the game with “proper” level generation, rather than selecting from a pool of known good seeds.

Proposed Solution

We decided that the best thing to do was to get the levels generating on background threads and to run several level generations in parallel. These threads can run before the player even unlocks the levels in question, so they will (hopefully) be ready to play immediately on selection. This also works to handle seed mining at run-time, as we can save good seeds back to the player profile.

We can’t run the entire generation in background threads though, as each level must be finalised and the GameObjects instantiated in unity’s main thread. This finalisation phase, that I call “building” the level, needs to be run before we can know the score of the level (and also to get an image of the island for the level icon). Levels that don’t score high enough have to go back to the threads to try generating with new seeds.

So this gives a system that consists of the following parts:

  1. A generation queue.
  2. A number of generation threads.
  3. A build queue.
  4. A single level builder.

Which can be visualised as the following schematic:

Or seen in action in this gif of super-ugly debug UI:

For this post, I’ll just be focusing on the individual generation threads.

Pausing, Resuming and Aborting Threads

Return to Blog