Introduction to Asynchronous Programming in Unity

Dive deeper into the world of asynchronous programming in Unity by creating a fun town-builder game. By Johnny Thompson.

5 (3) · 3 Reviews

Download materials
Save for later
Share

Video games are loop-driven applications, meaning that some code executes before a frame draws to the screen. As a result, games often have many things to process in only a fraction of a second. If anything takes longer than a few milliseconds to complete, the frame rate drops and creates a poor player experience. No one likes choppy gameplay!

But sometimes, it’s unavoidable for tasks to take longer than a few milliseconds to complete. This is where asynchronous programming can really come in handy. You can start processes in parallel, allowing the user to continue with their game.

In this tutorial, you’ll create Wenderlich-Topia, a town-building game that allows users to construct roads and houses asynchronously.

Wenderlich-Topia game in action

In the process of building the game, you’ll learn how to:

  • Build roads with basic asynchronous methods.
  • Trigger actions once the build process is complete.
  • Build houses with multiple asynchronous methods, returning the construction costs once builds complete.
  • Cancel asynchronous build methods.
  • Handle errors and catch exceptions.

You’ll need Unity 2020.3 or later to build the game.

Note: This tutorial assumes you understand the fundamentals of working with C# and Unity. If you need to familiarize yourself with Unity, read the Get Started with Unity tutorial before proceeding. If you need a C# primer, then you should check out Beginning Programming with C#.

Getting Started

Download both the starter and final projects by clicking the Download Materials button at the top or bottom of the tutorial.

Explore both projects and familiarize yourself with the setup. In the true spirit of asynchronous programming, you could also explore the projects in parallel with working on the tutorial. :]

Setting Up the Starter Project

Open the starter project in Unity and navigate to the Assets/RW folder. Here are the subfolders you see and what they contain:

  • Audio: Background music and the cash register sound effect.
  • Prefabs: Complete and partial structures that get built in the project.
  • Scenes: Wenderlich-Topia demo scene.
  • ScriptableObjects: Data for the structures that you’ll build.
  • Scripts: Scripts for all game logic.
  • Sprites: Sprites for all isometric art assets.

Open the demo scene located in the Assets/RW/Scenes/Wenderlich-Topia project folder. Set the aspect ratio of the Game View to 16:9 Aspect so the UI displays correctly.

setting the aspect ratio to 16:9 for the Game View

Click Play to start the game. You’ll see the Welcome UI. Click the “X” icon to close the welcome message and observe the starting scene.

wenderlich-topia game starting screen

Advantages of Asynchronous Programming

The main advantage of asynchronous programming is that you can run multiple tasks simultaneously without blocking the overall execution of the current thread. In Unity, unless you’re getting into the Job System, most gameplay code is synchronous. That is, the code executes sequentially — one line, one instruction at a time. Once a task is finished, another one begins. Generally, this works fine but not always.

For example, in the sample project, a road can take a few seconds to build. If the code were purely synchronous, the player would have to wait for the road to finish building before doing anything else. Building a city would take a lot of time!

This is where asynchronous programming can help.

Writing Basic Asynchronous Code

Using asynchronous code, you’ll allow players to build multiple houses and roads in Wenderlich-Topia asynchronously.

First, you’ll define an entry point to serve as a bridge between synchronous and asynchronous code. Then, you’ll define specific tasks that run asynchronously. And finally, you’ll write sections of code that will run once your tasks are complete.

To summarize the game’s construction loop:

  1. Player selects a type of structure from the menu.
  2. Player clicks on the map where they want to place it.
  3. A temporary construction tile spawns where the structure will go.
  4. The program takes some time to build the structure.
  5. Once time is up, the program replaces the construction tile with the finalized road or house tile.
  6. The program plays a UI effect to display the cost of construction and adds it to the total city cost.
Note: Some of the behaviors listed above already exist in the starter project. You can check out the StructurePlacementManager.cs and UiManger.cs scripts to see how behaviors 1, 2 and 6 work. You will, however, implement behaviors 3-5 later in the tutorial.

Defining the async Keyword

The keyword async, as you might have guessed, defines a method that can run asynchronously. async goes between the access modifier (public, private, etc.) and the return type.

To define async, open RW/Scripts/Managers/ConstructionManager.cs in your editor of choice. Add async to BuildStructure on line 48. Your BuildStructure method should now look like this:

public async void BuildStructure(GameObject placementStructure, Vector3 buildPosition)
{
    // Method code goes here
}

This method serves as the entry point into the remaining asynchronous code you’ll implement in this project. This entry point can await the completion of other asynchronous methods before proceeding.

Take note of the async method’s return type. (In this method’s case it is void). Methods tagged with async can have one of three return types: void, Task and Task<TResult>. All three return types will be covered in this tutorial.

Avoid using void as a return type for async except for event handlers or as an entry point into asynchronous code. This is because, while async void can wait for asynchronous methods to complete, other methods can’t wait for async void to complete, and will continue executing, leading to unexpected and out-of-order code execution.

Additionally, async void can’t catch exceptions in the same way async Task can. You’ll learn to catch exceptions later in this tutorial. For now, start building some structures in Wenderlich-Topia.

Building Structures With Task

By now you’ve seen the term Task a few times. But what exactly does it mean? In asynchronous programming, Task is simply a class that represents a single operation that can run asynchronously.

An async method that returns Task cannot return any values. However, unlike void, Task does allow you to check an operation’s completion status. You can execute code after the Task method is finished.

So, to allow your BuildStructure method to know when a road is finished building, you’ll need a method that returns a Task. Add a new method called BuildRoadAsync to ConstructionManager.cs, after the existing BuildStructure method:

private async Task BuildRoadAsync(RoadBuildProperties roadProperties, Vector3 buildPosition)
{

}

Here, the RoadBuildProperties argument is a pre-declared ScriptableObject that contains information pertinent to the construction of the road and buildPosition is the position that the road will go on the map.

Note: While not required by the compiler, it’s best practice to add the suffix Async to asynchronous methods, to denote them as asynchronous.
Johnny Thompson

Contributors

Johnny Thompson

Author

Srikar Mutnuri

Tech Editor

Yuliya Goldshteyn

Editor

Julia Zinchenko

Illustrator

Sean Duffy

Final Pass Editor

Tammy Coron

Team Lead

Mauro Fuentes

Topics Master

Over 300 content creators. Join our team.