As I’ve been working on the Radical Bowling project I’ve found myself using coroutines quite a bit. It seems like every time I have to look up the syntax, so I thought I would write a few quick examples to reference.
Example: Defer by time
In this example I can defer some behavior for the specified time. In this case I destroy the game object.
IEnumerator DestroySelf(float time)
{
yield return new WaitForSeconds(time);
GameObject.Destroy(this.gameObject);
}
Example: Lerp a value over time
In this example I Lerp the volume of an audio source from it’s starting volume to 0.0f over time.
IEnumerator FadeMusic(float time)
{
var audioSource = GetComponent();
float volume = audioSource.volume;
float currentTime = 0.0f;
do
{
var newVolume = Mathf.Lerp(volume, 0.0f, currentTime / time);
audioSource.volume = newVolume;
currentTime += Time.deltaTime;
yield return null;
}
while (currentTime <= time);
audioSource.Stop();
}
There are a lot of other ways that coroutines can be used but these are the two methods I've been using the most. Don't forget to call coroutines with the StartCoroutine() method.
Update: 4/18/2018
It's important to call Coroutines correctly. In my bowling game I created a bug when I made the DestroySelf() function public and called it from another class. Calling a coroutine from another class is supported and often a good idea, but in this case I had code elsewhere on my object that would destroy the game object before the coroutine could execute. Basically it went something like this:
- Step 1: Player throws ball - Step 2: Ball triggers collider and calls coroutine with 2 second delay - Step 3: Most of the time the ball rolls off the lane and is destroyed but another method
The issue was Step 2. I added this late last week to try to remove balls that didn't have enough remaining force to actually roll of the lane. I fixed this today by making a public function in the BallController class. I call this from my other class and it is responsible for starting the coroutine. Maybe not the most elegant solution, but this is a demo project after all.
public void DestroySelf(float time)
{
StartCoroutine(_DestroySelf(time));
}
private IEnumerator _DestroySelf(float time)
{
yield return new WaitForSeconds(time);
GameObject.Destroy(this.gameObject);
}
Update: 4/21/2018
This morning I ran into an instance when I needed to cancel a running coroutine. After checking the docs this is what I came up with. A variable named coroutine keeps track of the running instance. I can pass this to StopCoroutine to... stop the coroutine.
private IEnumerator coroutine;
public void DestroySelf(float time)
{
coroutine = _DestroySelf(time);
StartCoroutine(coroutine);
}
public void AbortDestroySelf()
{
if(coroutine != null)
{
StopCoroutine(coroutine);
coroutine = null;
}
}
private IEnumerator _DestroySelf(float time)
{
yield return new WaitForSeconds(time);
GameObject.Destroy(this.gameObject);
}