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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Introduction to Asynchronous Programming in Unity
35 mins
- Getting Started
- Setting Up the Starter Project
- Advantages of Asynchronous Programming
- Writing Basic Asynchronous Code
- Defining the async Keyword
- Building Structures With Task
- Placing Temporary Construction Tiles
- Adding await to Start Running Asynchronous Code
- Further Asynchronous Programming Concepts
- Returning Values From Tasks
- Awaiting Multiple Tasks
- Building a House by Parts
- Cleaning up Construction
- Improving Your Asynchronous Code
- Cancelling Tasks
- Cancelling a Task Efficiently
- Catching Exceptions
- Choosing Asynchronous Programming vs. Coroutines
- Advantages of async Over Coroutines
- Deciding When to Use Coroutines
- Where to Go From Here?
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.
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.
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.
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.
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:
- Player selects a type of structure from the menu.
- Player clicks on the map where they want to place it.
- A temporary construction tile spawns where the structure will go.
- The program takes some time to build the structure.
- Once time is up, the program replaces the construction tile with the finalized road or house tile.
- The program plays a UI effect to display the cost of construction and adds it to the total city cost.
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.