To access the GameObject data or instantiate new GameObjects, we need code that have access to the UI, ie. code that runs in the Unity Main thread.
I struggled during a weekend to find out why it is not possible to instantiate a GameObject from the timer.Elapsed delegate function. It turns out that the delegate runs in another thread as @Kurt-Dekker mentioned above.
Finally, I found a solution on StackOverflow, that forces the timer delegate to execute code in the same thread that created the Timer, using SynchronizationContext.Current:
Alternatively if you’re like me and you don’t want to use coroutines or have allocations:
Timer struct
using System;
using UnityEngine;
[Serializable]
public struct Timer
{
public float Duration;
public float IsRepeating;
private float nextTriggerTime;
/// <summary>
/// Indicates whether the timer has expired and is ready to trigger.
/// </summary>
public bool HasExpired
{
get
{
if (Time.time < nextTriggerTime)
{
return false;
}
else
{
if (IsRepeating)
Restart();
return true;
}
}
}
/// <summary>
/// Starts or restarts the timer.
/// </summary>
public void Restart()
{
nextTriggerTime = Time.time + Duration;
}
}
Example MonoBehaviour
using UnityEngine;
public class TimerExample : MonoBehaviour
{
private Timer timer;
private void Start()
{
timer = new Timer
{
Duration = 2.0f,
IsRepeating = true
};
timer.Restart();
}
private void Update()
{
if (timer.HasExpired)
{
// do stuff
}
}
}