informa
3 min read
article

Lovers in a Dangerous Spacetime DevLog #11: Timing Is Everything

Implementing an easy to use coroutine-based timer in Unity.

gif_ceraf

The Ceraf enemy uses timers to control its pre-shoot, shoot and post-shoot animations and actions.

 

One of the tasks we find ourselves doing quite frequently while working on Lovers in a Dangerous Spacetime is controlling the timing of things (loop an animation for x seconds, randomize AI behaviour every y seconds, etc.). There are many ways to accomplish these types of actions, for instance you could do something like this:

float timerLength = 1;
void Update()
{
  if(timerLength > 0)
  {
    timerLength -= Time.deltaTime;
    if(timerLength <= 0)
    {
      doSomeAction();
    }
  }
}

This works in the simplest cases, but it requires you to reuse the same code in any script that uses a timer and quickly becomes cumbersome if you require multiple timers in a script. You could use a coroutine with Unity's handy built-in wait function:

void Start()
{
  StartCoroutine(timerCoroutine());
}

IEnumerator timerCoroutine()
{
  yield return new WaitForSeconds(1);
  doSomeAction();
}

Coroutines are great for when you want a long sequence of actions or when you need to do something over a number of frames, but if you just want to wait for a certain amount time before performing an action, having to write a new method is tedious (plus, until recently they were very difficult to cancel). To get around these issues and help satisfy our laziness we created a class that to encapsulate the functionality of a countdown timer: CoroutineTimer.

Click to view Gist

As its name implies, CoroutineTimer utilizes Unity's coroutine library to provide a straightforward timer mechanism. In its simplest form, CoroutineTimer acts a straightforward timer:

CoroutineTimer timer = new CoroutineTimer(timerLength);
timer.Start(gameObject, doSomeAction);

After timerLength seconds, the doSomeAction method will be called. (Note that you must supply a GameObject to the timer's Start method as coroutines can only be run by MonoBehaviours and CoroutineTimer attaches a new MonoBehaviour to the supplied GameObject when it runs.) We've also included additional functionality to CoroutineTimer that comes in handy relatively frequently. For instance, say you wanted an enemy to shoot every 2 seconds, but to only start shooting initially after 4.5 seconds have passed. Also, you realize that it looks pretty mechanical if the enemy shoots *exactly* every 2 seconds, so you want to randomize the behaviour a bit so it actually only shoots every 1.8-2.2 seconds (i.e. a 10% randomization). Sure thing, no problem:

float length = 2f;
float randomizationFactor = 0.1f;
float startDelay = 4.5f;
bool repeat = true;
CoroutineTimer timer = new CoroutineTimer(length, randomizationFactor, startDelay, repeat);
timer.Start(gameObject, shoot);

CoroutineTimers can also be cancelled at any point (using the Stop() method) or reused once they are stopped finished. Additionally, it uses the [System.Serializable] attribute, so its properties can be serialized and exposed in the Unity Editor.

coroutine-timer-serialized

Limitations & peculiarities
Unfortunately there is no way to check how much time is left in a CoroutineTimer, you merely start it and it lets you know when it's done.

CoroutineTimer uses the string-based method of starting and stopping its coroutines. We're generally not big fans of this approach, but until Unity 4.5 it was the only way to easily cancel a running coroutine. Now that the StopCoroutine method can take an IEnumerator, we will likely update the class in the future to use that instead. This change would also negate the need to pass a GameObject to the timer (we are attaching a new MonoBehaviour for the timer only for safety since StopCoroutine(someString) stops all coroutine methods named someString on a given MonoBehaviour) and we could instead simply pass a MonoBehaviour.

Original post

// Adam Winkels (@winkels) is a co-founder of Asteroid Base.

Latest Jobs

Treyarch

Playa Vista, California
6.20.22
Audio Engineer

Digital Extremes

London, Ontario, Canada
6.20.22
Communications Director

High Moon Studios

Carlsbad, California
6.20.22
Senior Producer

Build a Rocket Boy Games

Edinburgh, Scotland
6.20.22
Lead UI Programmer
More Jobs   

CONNECT WITH US

Register for a
Subscribe to
Follow us

Game Developer Account

Game Developer Newsletter

@gamedevdotcom

Register for a

Game Developer Account

Gain full access to resources (events, white paper, webinars, reports, etc)
Single sign-on to all Informa products

Register
Subscribe to

Game Developer Newsletter

Get daily Game Developer top stories every morning straight into your inbox

Subscribe
Follow us

@gamedevdotcom

Follow us @gamedevdotcom to stay up-to-date with the latest news & insider information about events & more