How to Use System.Timers.Timer With unity GameObjects

Hi

System.Timers.Timer timer = new(2000);
        timer.Start();

        timer.Elapsed += delegate
        {
            Debug.Log("Tick");
            Debug.Log(gameObject.Name);
        }
;

It does not write gameobject name in console window, is there any solution for this?

Just use coroutines? Or, if you’re dead-set on using Timers, read the examples on the documentation to figure out how to implement them.

Find out how Timer works… does it call that delegate in another thread?

If so, that will NEVER work with Unity. With Unity 100% of EVERYTHING you do must be on the main thread.

Use coroutines.

Coroutines in a nutshell:

Splitting up larger tasks in coroutines:

Coroutines are NOT always an appropriate solution: know when to use them!

Our very own Bunny83 has also provided a Coroutine Crash Course:

https://answers.unity.com/questions/1749615/coroutines-ienumerator-not-working-as-expected.html?childToView=1749714#answer-1749714

2 Likes

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:

https://stackoverflow.com/a/68618438/9567003

Developing on top of that it is possible to force the delegate to run code in the Unity Main thread:

https://stackoverflow.com/a/78633738/9567003

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
        }
    }
}
1 Like