Screenshot Coroutine Demo

Waiting for Simulation Results using Coroutines in Unity3d

In an algorithm that I am implementing for my thesis, I need to wait for information from the simulation before I can continue the algorithm. In a multi-threaded application you could just create multiple threads with chunks of work that have to wait until other work is finished. But without multithreading there is a problem: How do you wait for something without blocking the entire Application while still tracking the state of an algorithm?

This could be done by storing everything in a class and using complicated constructs to continue from the correct state. For a simple algorithm this would be feasible, for my requirements it isn’t. Coroutines in Unity3d allow for a relatively elegant (and readable) solution. At the time, I hadn’t fully grasped what Coroutines could (and could not) do, so I implemented a simple case study (download at the bottom of the page) to help me understand. Lets look at the problem:

The Problem

Assume we have an algorithm that does some calculations, runs a (physics) simulation and then does some more calculations based on the results of that simulation.

To make things clearer, lets define the requirements of our simplified case study: The algorithm is the Master, it generates n balls (Slaves) that will fall onto a plane below them. Each Slave writes a result and once all the balls have collided with the plane, the algorithm can continue.

So the algorithm can not continue as long as the physics simulation isn’t done.

How can we make sure the algorithm does not block the rest of the application, but waits for the results of the simulation?

Coroutines

For this particular case we can probably get by with a single if statement, but once we have more complicated constructs this will quickly make the code unreadable. Coroutines seemed a bit like dark magic to me at first, but they are  extremely helpful! I am not going to go into any  detail about how Coroutines work, there is a great article by Richard Fine. It really helped me understand the concept.

Basically a Coroutine can pause itself at any point – and return to that point in the next frame. Sometimes you don’t want to do that so Unity has YieldInstructions such as WaitForSeconds, WaitForEndOfFrame etc. that I am not going to into Detail here, the important thing is that we can leave a method – continue the simulation – and continue that method at a later point in time, from the correct place in the method.

The Solution

So maybe we can make the algorithm a Coroutine, pause it after the simulation is started and continue when the simulation is ended.

First, lets write the Slave (Ball) script. All it needs to do is to detect the collision, write a result to the Master and destroy itself.

For demonstrative purposes, I am going to keep things simple so this is not production quality code.

[RequireComponent (typeof (Rigidbody))]
public class Slave : MonoBehaviour {
	public Master _master;

	void OnTriggerEnter(Collider other) {
		Debug.Log("Collision");
		_master._collided++;
		GameObject.Destroy(gameObject);
	}
}

OnTriggerEnter is called when the Rigidbody collides with a Trigger Collider. So the Slave (A rigidbody) will detect a collision, tell it’s Master about it, and destroy itself.

The Master script includes the algorithm and I won’t post the class in full (you can download it further down). Lets begin with the method that starts the algorithm (and the simulation).

void StartAlgorithm() {
	_collided = 0; //reset counter

	//generate the slaves at random positions
	for(int i=0; i<_nSlaves; i++) {		
		GameObject go = (GameObject) Instantiate(_slavePrefab);
		go.transform.position = RandomPosition();
		Slave slave = (Slave)go.GetComponent<Slave>();
		slave._master = this;
	}

	StartCoroutine(Algorithm());

	Debug.Log("Algorithm started");
}

The method StartAlgorithm() first resets the _collided counter (a public member of Master) and then generates the n Slaves at random positions above the plane. Now that we have initialized the simulation, we can start the algorithm itself. This is done with the StartCoroutine(Algorithm()) call. The important thing is that StartCoroutine will return without executing the Algorithm method itself. The coroutine is added to a collection of coroutines that the engine will execute at a later point in time (after Update see MonoBehaviour Lifecycle).

Lets take a look at the algorithm itself:

IEnumerator Algorithm() {
	/** do some preliminary calculations  **/

	//wait for all slaves to collide
	while(_collided < _nSlaves) {
		yield return null; //pause and return next frame
	}

	/** all slaves have collided, do more calculations with the result **/
}

A coroutine needs to return IEnumerator and cannot take reference or output parameters. But the important part is the while loop. It runs until the _collided counter reaches the number of slaves that we generated in the StartAlgorithm method. Normally, this would block the application since _collided cannot increase as long as we are in the loop. Luckily for us, we can leave the function to continue it at a later time using the yield return statement.

The simulation will now run and the engine will return to the coroutine every frame to check if all slaves have hit the plane.

The benefits are that we can now write a complicated algorithm and easily specify points where it will yield (effectively pause), so that the rest of the application can continue running. The case study does not have a real purpose – but we could write the time it took for the ball to reach the plane to the master and calculate the average time and do something with that. There are many applications when you have an algorithm that needs to wait for some result or event to happen. The above example is one of the simplest methods of achieving this, although if you look into Coroutines in more detail, there is a lot more to explore. I would love to see if anyone comes up with a more elegant solution, or another problem that can be solved using Coroutines.

Download the Unity Package

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>