[Solved] Cannot delete timer.

I’m trying to create a timer that expires every X seconds. Upon expiring, it casts a spell. It does this X number of times then deletes itself. But no matter what I do my timer always remains and is never destroyed. I also get the error “…can only be called from the MainThread.”

using UnityEngine;
using System.Collections;
using System.Timers;

public class AttackBehaviour : MonoBehaviour
{

        public bool canAttack;
        public double attackInterval;
        public Spell[] spells = new Spell[1];
        public int numberOfAttacks;
        int attacks;

        Vector2 targetPos;

        Timer timer;

        void Awake ()
        {
                timer = new Timer (attackInterval);
                timer.Elapsed += OnTimerEnds;
                timer.Enabled = true;
                targetPos = new Vector2 (0, 0);
                spells [0].Cast (targetPos);
        }

        void OnTimerEnds (object source, ElapsedEventArgs e)
        {
                if (attacks < numberOfAttacks) {
                        Debug.Log ("Frozen Orb Nova");
                        //int i = Random.Range (0, spells.Length - 1);
                        //spells [0].Cast (targetPos);
                        attacks++;
                        timer.Elapsed -= OnTimerEnds;
                        timer.Dispose ();
                        timer.Stop ();
                        timer.Enabled = false;
                } else {
                        Debug.Log ("Over.");
                        timer.Dispose ();
                        Destroy (this);
                }
        }
}

Timer in C# are executed on a separate thread than the one from where they are called. Unity functions like Destroy doesn’t work outside the main thread.

I suggest that you use Coroutines instead of a timer for what you are trying to achieve. It would be something like this (may contains error, I didn’t check inside unity) :

        void Awake ()
        {
               StartCoroutine(SuccessiveAttacks());
        }
 
        IEnumerator SuccessiveAttacks ()
        {
                while(attacks < numberOfAttacks) {
                        Debug.Log ("Frozen Orb Nova");
                        //int i = Random.Range (0, spells.Length - 1);
                        //spells [0].Cast (targetPos);
                        attacks++;
                       
                        yield return new WaitForSeconds(attackInterval);
                } 

                Debug.Log("Over.");
                Destroy(this);
        }
1 Like

Ooo I forgot about co-routines. That’s a good idea. But how come the timer isn’t destroyed when I call “Dipose”, “Stop”, amd “Enable = false” on it?

What do you mean by destroyed ? Freed from memory ? Or only stopped ?

I never used a timer myself, but object in C# will be kept in memory until there is no script that reference it. Garbage collector will then delete it and free the memory. You can unreference it by destroying the script that reference it or forcing its value to null.

But judging the documentation, Dispose should suffice to completely destroy the timer in case you never use it again later. I’m no expert with this so I hope that somebody else may give you a better answer.

Freed from memory AND stopped.

Yea according to the docs, Dispose should should stop it because it destroys it. In the original script I posted, one can see I tried every method to delete the timer but it doesn’t delete it.

bump

You still need to do this in your else clause:
timer.Elapsed -= OnTimerEnds;
Otherwise, even though it’s disposed, the GC won’t clean it up.