[Solved] Repeatedly invoke a method with parameters every X period of time without MonoBehaviour?

Any hint, advice, or pointers would be greatly appreciated. :slight_smile:

Best pointers starts -I think- with asking why you think you need that ?

That can give you hints to the anwser.

I’m writing an extension method for Transform that modifies the size of the transform over a period of time. I want to be able to do a simple “transform.Grow(maxSize: 5f, rate: .5f);”. Also I think it’ll give me good insight into delaying method calls (something I need to do for my ability system).

If your transform isn’t going to be de-activated you can use StartCoroutine and Invoke.

Other option is to spawn your own GO to have as much control over its lifetime as you can.

StartCoroutine and Invoke rely on MonoBehaviour though.

I was thinking of using tasks but it seems it’s not available in Unity.

Ah right, you’re in extension method sorry.

You can do threads but i don’t think you’ll be able to scale your object from it, all you can do with it is calculation/processing then pull data back to main thread so it’s of no use in your case.

Then what about making a singleton of a component that spawns itself on a new GO if it doesn’t exist ?

Yea I wanted to do threads but apparently I can’t touch anything from Unity with them. :confused:

I’ll try the Singleton approach although I kinda wanted to avoid it.

Try looking at System.Timers, although I would suggest the Singleton approach instead.as you would want to avoid making new threads

one solution/workaround to that is to use loom to queue on main thread

example of me using it with parse threads

public static void Register(string email, string password)
{
    try
    {
        ParseUser user = new ParseUser() { Username = email, Email = email, Password = password };
        user.SignUpAsync().ContinueWith(t =>
        {
            if (t.IsFaulted || t.IsCanceled)
                Loom.QueueOnMainThread(() => { RegisterFail(t.Exception); });
            else
                Loom.QueueOnMainThread(() => { RegisterSucceed(); });
        });
    }
    catch (Exception e)
    {
        RegisterFail(e);
    }
}

loom code from here:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using UnityEngine;
public class Loom : MonoBehaviour
{
    public static int maxThreads = 8;
    private static Loom _current;
    private static bool initialized;
    private static int numThreads;
    private List<Action> _actions = new List<Action>();
    private int _count;
    private List<Action> _currentActions = new List<Action>();
    private List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
    private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
    public static Loom Current
    {
        get
        {
            Initialize();
            return _current;
        }
    }
    public static void QueueOnMainThread(Action action)
    {
        QueueOnMainThread(action, 0f);
    }
    public static void QueueOnMainThread(Action action, float time)
    {
        if (time != 0)
        {
            lock (Current._delayed)
            {
                Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action });
            }
        }
        else
        {
            lock (Current._actions)
            {
                Current._actions.Add(action);
            }
        }
    }
    public static Thread RunAsync(Action a)
    {
        Initialize();
        while (numThreads >= maxThreads)
        {
            Thread.Sleep(1);
        }
        Interlocked.Increment(ref numThreads);
        ThreadPool.QueueUserWorkItem(RunAction, a);
        return null;
    }
    private static void Initialize()
    {
        if (!initialized)
        {
            if (!Application.isPlaying)
                return;
            initialized = true;
            var g = new GameObject("Loom");
            _current = g.AddComponent<Loom>();
        }
    }
    private static void RunAction(object action)
    {
        try
        {
            ((Action)action)();
        }
        catch
        {
        }
        finally
        {
            Interlocked.Decrement(ref numThreads);
        }
    }
    private void Awake()
    {
        _current = this;
        initialized = true;
    }
    private void OnDisable()
    {
        if (_current == this)
        {
            _current = null;
        }
    }
    private void Update()
    {
        lock (_actions)
        {
            _currentActions.Clear();
            _currentActions.AddRange(_actions);
            _actions.Clear();
        }
        foreach (var a in _currentActions)
        {
            a();
        }
        lock (_delayed)
        {
            _currentDelayed.Clear();
            _currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time));
            foreach (var item in _currentDelayed)
                _delayed.Remove(item);
        }
        foreach (var delayed in _currentDelayed)
        {
            delayed.action();
        }
    }
    public struct DelayedQueueItem
    {
        public Action action;
        public float time;
    }
}
1 Like

Attach a MonoBehaviour and run your functionality there with a coroutine or invoke repeating. There is no particular reason not to use a MonoBehaviour. Right tool, right job.

1 Like