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?
Placing Temporary Construction Tiles
Next, you’ll start adding some functionality to the new BuildRoadAsync
method. You do this the same way you would add steps to any regular method.
For starters, you’ll place a construction tile at the location where the road will be. In BuildRoadAsync
, add the following line to instantiate constructionTilePrefab
at the location the user clicks.
var constructionTile = Instantiate(constructionTilePrefab, buildPosition, Quaternion.identity, levelGeometryContainer);
Now that you’ve defined the first step of building a road, it’s time to call the method. You call asynchronous methods the same way you call any other method. In the BuildStructure
method, add the following line after roadProperties
gets defined and set:
var buildRoadTask = BuildRoadAsync(roadProperties, buildPosition);
buildRoadTask
will now hold a reference to the returned Task
from the BuildRoadAsync
method. Save your script.
Test if this first step worked. Go to Unity and click Play. In play mode, select one of the roads from the build menu and place it in the game world. If everything is working correctly, you’ll see the construction tile spawn where you placed the road.
Your code is still running synchronously. That’s because the program doesn’t know to wait for a task’s completion to run further steps.
Adding await to Start Running Asynchronous Code
The final component for writing basic asynchronous tasks is await
. Using it with a Task
tells the program to return to executing the synchronous code that called the async
method. This synchronous code is therefore not blocked, and can continue running at least until it requires the returned Task
code. The async Task
will continue running in the background while the rest of the synchronous application code runs. Once the async Task
method completes, the code after await
will resume executing.
The above might sound confusing at first, so read it a few times. Or read this quote from the Microsoft await operator documentation:
In Wenderlich-Topia, roads will take some time to build, yet the player will expect to be able to continue interacting with the game while the road is under construction. The code will await
the road’s completion so that the rest of the game can continue running uninterrupted.
Once the road building task is complete, the asynchronous method resumes and does the following:
- Removes the temporary construction tile.
- Spawns the final road tile.
- Plays a UI effect displaying the cost of the road.
To start running asynchronous code, you’ll await
a call to Task.Delay(int millisecondsDelay)
in the BuildRoadAsync
method to allow a time delay of 2.5 seconds after the constructionTile
spawns. Add the following to the end of the BuildRoadAsync
method.
await Task.Delay(2500);
Now, any subsequent code in BuildRoadAsync
will not run until the 2.5 second timer is up.
Next, remove the temporary construction tile and spawn the final road tile. To do this, add the code below after the await Task.Delay(2500);
line in the BuildRoadAsync
method:
Destroy(constructionTile);
Instantiate(roadProperties.completedRoadPrefab, buildPosition, Quaternion.identity, levelGeometryContainer);
The full road construction cycle is now in place. Next, you need to ensure that BuildStructure
knows when the road is finished so it can show the relevant UI effects. The UI effect for showing the total build cost is already included in the Starter project. The UI Manager handles this responsibility for you.
Add the code below to the BuildStructure
method, after the call to BuildRoadAsync
, which you added previously:
await buildRoadTask;
uiManager.NewStructureComplete(roadProperties.roadCost, buildPosition);
The game will now wait for the BuildRoadAsync
to complete and then show the cost of the road as a UI effect.
Go to Unity, enter play mode, and place some roads in the game world.
You’ll see the construction tile appear. Then, after 2.5 seconds, the final road will appear, followed by the UI effect showing the cost. Notice that while the road is being built, you can still interact with the game and even build multiple roads at once if you’re fast enough. :]
Further Asynchronous Programming Concepts
In the following two sections, you’ll learn some more asynchronous programming features by building houses. In the end, residents of Wenderlich-Topia will have a place to live.
To build a house, implement the following steps:
- Place a temporary construction tile where the house will get built.
- Build a house frame.
- Build a roof.
- Build a fence.
- Finalize the house.
- Play UI effect to display final cost.
Steps 2-4 are individual tasks, but they won’t happen in sequence. Once you construct the frame, you can also construct the roof and fence at the same time. Once both the roof and fence are complete, you can finalize the house.
Returning Values From Tasks
Remember the three types of return values you learned about earlier? In the following section, you’ll learn how to use the third type, Task<TResult>
to return values from Tasks.
One key difference between building houses and roads in Wenderlich-Topia is the cost. The player won’t know the final cost of building a house at the time of construction, but they will know the estimated cost shown on the Build House UI button.
In ConstructionManager.cs, define a BuildHouseAsync
method to schedule individual tasks. Each task calculates the task cost by multiplying the task completion time by a wage value. At the end of BuildHouseAsync
, you’ll:
- Sum the cost of all tasks.
- Return the total cost to
BuildStructure
. - Display the total cost UI effect.
You’ll now implement the method that returns Task<TResult>
. This return type has the same features as a standard Task
, with the important benefit of being able to return a defined value within the angle brackets. Like a standard non-void method, async Task<TResult>
must return a value of the defined type.
Now, define the BuildHouseAsync
method that returns a Task<int>
, as the total house cost will be represented as an integer type:
private async Task<int> BuildHouseAsync(HouseBuildProperties houseBuildProperties, Vector3 buildPosition)
{
var constructionTile = Instantiate(constructionTilePrefab, buildPosition, Quaternion.identity, levelGeometryContainer);
return 100;
}
The method spawns the temporary construction tile and returns a value of $100. The value is temporary. You’ll calculate the actual value of the house once it’s finished. Too bad because a $100 house would definitely be a steal.
Next, in BuildStructure
, make a call to BuildHouseAsync
after you set houseProperties
. Store the result as a Task
, then wait until it’s complete:
var buildHouseTask = BuildHouseAsync(houseProperties, buildPosition);
await buildHouseTask;
Next, in the same method, calculate the cost of the house by accessing Result
on buildHouseTask
. You’ll use that value to display the UI effect on the screen.
var houseCost = buildHouseTask.Result;
uiManager.NewStructureComplete(houseCost, buildPosition);
Return to Unity, enter play mode, and attempt to build a new house in the game world. First, you’ll see the temporary construction tile placed on the map, immediately followed by the UI effect displaying $100, the placeholder cost.