1
\$\begingroup\$

I'm using Unity but creating my own process that has it's own coroutine process. So I can't use StartCoroutine() but I'm interested in how that method works since it's easier to use than me getting the IEnumerator and stepping over it inside my function that's already called as a coroutine.

How can I create some kind of StartCoroutine() type function for users to use instead of having them step over the IEnumerator manually. So for example below I step over the IEnumerator Utilities.Wait() function manually. How could I just call that and have it do that for me and wait the specific amount of time?

public IEnumerator reduceHealth(dynamic args)
    {
        int result = Health - args.reductionValue;
        Debug.Log("Before health: " + Health);
        Debug.Log("Reducing health");

        // reduce health over time
        while (Health > result)
        {
            Health -= 1;
            Debug.Log("Health = " + Health);

            // how can I simplify this for users of this library
            var co = Utilities.Wait(2000);
            while (co.MoveNext())
                yield return null;
        }

        // snap to end result
        Health = result;

        // tell everyone we've been hurt!
        //onHurt.Raise(new { value = health });
    }


public class Utilities
{

    public static IEnumerator Wait(int ms)
    {
        var watch = System.Diagnostics.Stopwatch.StartNew();

        long lastTime = watch.ElapsedMilliseconds;
        while (watch.ElapsedMilliseconds <= lastTime + ms)
        {
            yield return null;
        }

        watch.Stop();
    }
}
\$\endgroup\$
4
  • 2
    \$\begingroup\$ What is the actual question? How "StartCoroutine()" works internally? \$\endgroup\$ May 10, 2018 at 3:02
  • \$\begingroup\$ I think you forgot the include the question itself. \$\endgroup\$ May 10, 2018 at 11:56
  • \$\begingroup\$ @S.TarıkÇetin Sorry, updated with a more clear question. \$\endgroup\$
    – user441521
    May 10, 2018 at 13:28
  • \$\begingroup\$ @user3797758 Updated the question with a more clear question. \$\endgroup\$
    – user441521
    May 10, 2018 at 13:29

1 Answer 1

1
\$\begingroup\$

I don't think you should implement a delay like this -- if you have a lot of things waiting then effectively you have a lot of busywork stepping into each one and waiting for it to do nothing and return.

The standard way to do this in C# (that isn't the Unity Coroutine stack) is to use Task<T> and await instead of IEnumerable and yield, since that gets you the core Task event loop, which can use efficient abstractions based on TimerQueue and IOCP:

public async Task reduceHealth(args) { 
    log("Taking damage!");
    while(health > target) { 
       health -= attackvalue;
       log("current health {0}", health);
       await Task.Delay(2000);
    }
    health = target;
    log("I've been attacked!");
}

If you still want to build your own Coroutine machinery on generator expressions, you could build a core "event loop" thread that runs your coroutines, so your IEnumerables can return some kind of Task-like value that it can interpret.

I've added a TIO demo showing a basic POC of how you can build such a system. I'd still recommending sticking with existing coroutine solutions, but the general idea is to model Task-based composition and delays using subclasses of Task:

// example function
static IEnumerable<MyTask> reduceHealth(int health, int target, int atk) {
    Console.WriteLine("start");
    while (health > target) {
        Console.WriteLine($"{MyDelayTask.sw.Elapsed} health: {health}");
        health -= atk;
        yield return MyDelayTask.Delay(100);
    }
    Console.WriteLine("done");
    yield return new MyFinishedTask();
}

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ Interesting idea. I found this article that explains async-await in Unity. Doesn't seem 100% native support for it yet but there is a way. stevevermeulen.com/index.php/2017/09/… I'll consider playing around with it. For now I'm just curious on the coroutine. So These methods are part of my own Components. I hook up my own event type object to these methods and when they are called they are done so as coroutines. So I already have a global event loop for those but for some reason couldn't figure out how to do it locally to a comp like in my example. \$\endgroup\$
    – user441521
    May 10, 2018 at 14:39
  • \$\begingroup\$ I've added a link to a basic Proof of concept NIH-type Task framework demonstrating what I mean by "your IEnumerables can return some kind of "Task"-like value" \$\endgroup\$
    – Jimmy
    May 10, 2018 at 15:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .