Extending IEnumerator for "custom" coroutines

Heaps of topics about coroutines lately so I though I would post a little snippet. The TimedAction class triggers an action after a certain amount of seconds. What’s interesting is that you can query its state as it executes so you can drive a timer, progress bar, wrist watch, or whatever else you want. Of course you could do this in your coroutine but then it wouldn’t be very generic.

public class TimedAction: IEnumerator {
	float delay;
	Action action;
	
	private bool startedDelay;
	private DateTime endTime;
	
	public TimedAction(float delay, Action action) {
		this.delay = delay;
		this.action = action;
	}
	
	public void Reset() {
		startedDelay = false;
	}
	
	public bool MoveNext() {
		// If this is the first invocation we want to set our end time.
		if (!startedDelay) {
			endTime = DateTime.Now.AddSeconds(delay);	
			return true;
		} else {
			//On the second invocation do the action.
			action();	
		}
		// All done.
		return false;
	}
	
	public object Current {
	    get {
			if (!startedDelay) {
				startedDelay = true;
				// Return WaitForSeconds, Unity will handle the delay for us.
				return new WaitForSeconds(delay);
			}
		// If we have already delayed do nothing.
	        return null;
	    }
	}
	
	public DateTime EndTime {
		get { 
			return endTime;
		}
	}
	
	public TimeSpan TimeRemaining {
		get { 
			TimeSpan result = endTime - DateTime.Now;
			// The frame times probably won't lineup exactly with our delay, lets not allow negatives
			if (result < TimeSpan.Zero) result = TimeSpan.Zero;
			return result;
		}
	}
	
	public float Percentage {
		get {
			TimeSpan span = endTime - DateTime.Now;
			if (span < TimeSpan.Zero) span = TimeSpan.Zero;
			return 1.0f - (float)(span.TotalSeconds / delay);
		}
	}
}

And a behaviour to test it:

public class StartWorker : MonoBehaviour {
	
	public TimedAction action;
	
	void Start(){
		action = new TimedAction(4.0f, new Action(SayBoom));
		StartCoroutine(action);
	}
	
	public void Update(){
		// Of course you probably don't want to check this every frame, just an example
		Debug.Log (string.Format("Time: {0} ({1:##}%)", action.TimeRemaining, action.Percentage * 100.0f));	
	}
	
	public void SayBoom(){
		Debug.Log ("Boom");	
	}
}

Although this example is pretty trivial you can use a similar sort of approach for all kinds of cool stuff :slight_smile:

EDIT:

To be clear there is nothing (functionally) this approach enables that can’t be achieved with “normal” coroutines wrapped in another class. This approach tends to lead to neater code when you want to do things like control and interrupt the execution flow (e.g. pause) or provide results from the coroutine at any point during its execution (if you have an interface why not use it). This approach also allows you to inherit behaviour between coroutines including the functions Reset() and MoveNext() and the property Current. This is something you cannot do when using the auto-generated code of coroutines generated with yield.

1 Like

What’s the point of this?

public delegate void ActionDelegate()

Not quite sure if you are asking what a delegate is or why I’m using my own one (instead of Func or Action).

Presumably the second … this is just a matter of me cutting this code down from a more complicated example I’m actually using (in which the delegate has a more specific signature). In this case I could have (and should have) used Action given that it is already defined.

Just in case you are asking first (or someone else does), delegates work like function pointers in C, they allow you to pass a function as an argument. This way I can call my SayBoom() function or any other with a matching signature as a TimedAction.

Yes.

This is a cool sample, so don’t get me wrong. But you’re not actually extending IEnumerator - it’s just an interface, it has no class to extend. You have written a class that implements IEnumerator. Still a good example of how interfaces are used.

Indeed, I’m well aware of that, will update. I had trouble with the title so I think as soon as I had something vaguely conveying what I wanted I hit enter :slight_smile:

The point is not really to demonstrate interfaces; the point is that most people use functions containing yields for their coroutines almost exclusively (meaning that they are auto-generated). This approach gives you an alternative which has a lot of flexibility (e.g. you could implement a handler for a sequence of actions which you could pause, reverse, etc).

There’s nothing complex about it, but it’s just not something people seem to think of using very often.

I like the idea… but I think it’s overly verbose for what you’re trying to do :stuck_out_tongue:

I’d look into ‘token’ objects [no idea if that’s what they are actually called] and possibly using your own ‘StartCoroutine’ that accepts IEnumerator.

That said, I’ve not done this at a high level before.

Nice post. Not taken the time to look at it properly yet, I only just quickly skimmed it but just also wanted to point you towards a post Renauld Benard made about custom coroutines in Unity over here in case you want to mash up something between what he posted and you have here.

I think it’s pretty bad design, to access the fields directly. Adding an Update events where interested objects can subscribe for seems more reasonable approach.

I completely disagree. Events make sense for well … “events”, some kind of important state change. Events might make sense for a sequence of actions (e.g. event when each finishes), or you might use an event at the end of this instead of taking a delegate approach. But to use events to do something like a timer seems crazy. You want to send an event every tenth of a second to drive a stop watch readout?

It doesn’t allow you to “access the field directly”, it provides read-only properties which describe the state of the enumerator.

There’s no variable named “TimeRemaining” or “Percentage”. They’re both getter functions that returned calculated values. As such, there’s no problem accessing them directly. Furthermore, events aren’t guaranteed to arrive within a set time. You might get nothing for a whole 10th of a second, then two events arrive at once in a single frame. For a timer display, that’s not ideal.

Nevertheless, it’s wise to stick with the “KISS” principle. Using an additional event / observer mechanism is all well and good, but it’s overkill for an illustration of the core technique. JohnnyA clearly didn’t intend for this to be used in production.

That said, “Percentage” is definitely misnamed. It should return an actual percentage, not a value between 0 and 1!

Off topic but isn’t percentage usually expressed as a value between 0 and 1 (until display time). I usually see it done that way and have always done it that way. Presumably so if you use it for calculations you get a correct result. Obviously production code would have this documented.

i.e. 10 * 50% = 5 not 500

EDIT: Also +1 to this “JohnnyA clearly didn’t intend for this to be used in production”. I just wanted to illustrate a simple technique that I think is easily forgotten because of the typical way coroutines are used.

EDIT 2: The reason I added the observer pattern was to provide some kind of use case which made sense in a gaming context. I figured if I just demonstrated the core technique in isolation I would have written 40 lines of code to do what could have been done with 5, which might put people off a little :slight_smile:

That’s exactly what happens, as the time passes the event (percentage) changes. But instead of rising an event, you access the fields directly from the TimedAction object. The event can be useful if you want to create casting/progress bar, you would simply

TimedAction action = ...;
action.ProgressUpdate += delegate(float percentage, float timeLeft, float totalTime) { // or use the default EventHandler object/EventArgs if you like
    // update the progress bar here
}

And you even don’t have to store a reference of TimedAction, as long as you unsubscribe all listeners when you’re done (so it can be claimed by the GC).

Then the way you have seen it done is wrong. Your example is flawed because you do not return a percentage [fraction of 100]. If a function advertised that it returned a speed in m/s, but gave you km/s would you be happy? While it’s not drastically different, and can be easily modified, it’s not what the function claims to be.

Moving on.

While I certainly see what you’re trying to do, I’ve answered similar questions using a completely different line of reasoning and thought I’d post a quick example:

using System;
using System.Collections;
using UnityEngine;

public class AdvancedCoroutines : MonoBehaviour
{
    IEnumerator Start()
    {
        var isDone = new IsDoneObject();
        StartCoroutine(MyActions.DelayedAction(1, () => print("HUZZAH!"), timeRemaining => print(timeRemaining), isDone));
        while (!isDone) yield return null;
        print("Finished");
    }
}

public static class MyActions
{
    public static IEnumerator DelayedAction(float delayTime, Action action, Action<float> timeRemaingUpdate = null, IsDoneObject isDone = null)
    {
        while (delayTime > 0)
        {
            if (timeRemaingUpdate != null) timeRemaingUpdate(delayTime -= Time.deltaTime);
            yield return null;
        }

        action();
        if (isDone != null) isDone.SetDone();
    }
}

public class IsDoneObject
{
    private bool isDone = false;

    public void SetDone()
    {
        isDone = true;
    }

    public static implicit operator bool(IsDoneObject x)
    {
        return x.isDone;
    }
}

I use an Action to demonstrate active updates [think ‘events’] and an isDone object for passive updates [‘querying’]. I even rewrote it do be more generic ‘DelayedAction’ rather than the original ‘DelayedShout’.

What I like about this particular solution is that it’s shorter, and more to the point 44LOC vs 82LOC.

I used very similar code to actually run a ‘multithreaded’ downloaded in unity - where I could limit how many threads run at once, have a nice updating gui etc. without having to worry about threading etc.

Cant beleive we are still arguing about this trivial example, I think its time for me to stop trying to be useful here.

@Tseng, you don’t seem to understand (or don’t want to understand) what myself and Stimarco (one of the best and most helpful coders on the forums) have pointed out. Yes an event might make sense if you have a fixed set of intervals, however the provided approach is much more appropriate in most use cases involving timers and progress bars (for the reasons mentioned by myself and Stimarco).

Not to mention the first post clearly states this is a “pretty trivial” example.

@NPSF First comment not aimed at you, I don’t mind reasonable discussion, however:

  1. I’m well aware that percentage are generally displayed as a value between 0 and 100 followed by a trailing % sign. However I was being a little tongue in cheek when I asked “but isn’t percentage usually expressed as a value between 0 and 1”. The answer is yes it is. This is not me introducing some strange convention. When coding it’s more common to express percentages as a value between 0 and 1 than a value between 0 and 100. Google it or see for example this stack overflow post.

A few examples: The C# string formatter expects values between 0 and 1 for a percentage, (from the MSDN doc “P → Result: Number multiplied by 100 and displayed with a percent symbol.”) . Even MS Excel, aimed at a pretty general audience, uses 0 - 1 for its storage value for percentages (write 0.1, 10, 0.5, 50 in a column in Excel and convert them to a percentage).

  1. Your example doesn’t actually demonstrate what I was trying to demonstrate. You are just writing a normal coroutine and wrapping it in another coroutine. The point was never that you can’t write this example with less lines of code. Notice how the provided example doesn’t use yields, yet I’m still able to return WaitForSeconds and have it handled correctly by Unity.

I have no problem with using decimals 0-1, but it’s not a percentage - as per the definition of the term. The MSDN doc you linked does NOT use a percentage as an input - it accepts a number that it then converts to a percentage. Anywho, it’s a minor point.

While my code doesn’t demonstrate what you demonstrate, it does achieve the same end goal. I would point out that there’s only one coroutine in my example [same as yours]. And while I do you use ‘yield’ - you do to albeit with the more verbose ‘MoveNext’ and ‘Current’ functions.

Basically I’m trying to point out that since the compiler will already do this rewriting for you, why not use it? I find the use of token objects or events to lead to shorter code that achieves the same objective. It’s also more open to reuse - though i guess you could use interfaces or inheritance to allow multiple ‘actions’ to have a ‘percent completed’ function.

At the end of the day though, it’s just and alternative - feel free to ignore :stuck_out_tongue:

Yeah MSDN example wasn’t the best however it really is common to use 0-1 not 0-100. I tutored and later lectured programming for quite a while, and been in the industry for nearly 15 years, 10 of those in development and most of that as a development lead with 5-10 people reporting in to me. In that time I’d say that 0-1 has been used for functions labelled as percentage around .9 of the time (grin)*. It really is common, and I don’t think just its just in the areas I’m most familiar with. The fact the Stimarco and yourself think otherwise just points out how dangerous even the simplest of assumptions can be, document, document, document!

In terms of the example, it is difficult to find a clear cut case for this method being better than another (although I think the code comes out a little easier to understand in some cases). One quality, that I have pointed out in the first post, is that this lets you use inheritance to extend the enumerator. This is actually pretty handy and something you can’t do very easily using the “normal” way. Consider for example building up a library of handy IEnumerators like SequenceOfActions or DelegatedWork, etc. You could very easily extend these classes just overriding one or two methods to make a ReversibleSequenceOfActions or a PausableDelegatedWork.

In the end though, this was merely about saying “Hey guys, have you thought about doing this, it might help you elegantly solve a problem one day”.

  • Not trying to big note, just saying I have sat through a lot of code reviews and API selection panels… I’ve seen a lot of code :slight_smile:

This whole thing is quite interesting, because it demonstrates how programmers think of percentage as ‘number between 0 and 1’ when it’s not - as 200% is perfectly valid. The best match for what we actually mean that I could fines is ‘Unit Interval’ - but that’s verbose and not many people would intuitively know what it means. Anywho…

I would point out that both solutions can do this, you’d override the ‘IEnumerator’ while I’d override the ‘token object’. For example:

//untested
var sequence = new SequenceToken(()=> print("one"}, () => print("two"), ()=> print("three"));
StartCoroutine(Actions.Sequance(sequence));  //Accepts ISequenceToken
print(sequence.Peek());

var reversible = new ReversibleToken(()=> print("one"}, () => print("two"), ()=> print("three"));
StartCoroutine(Actions.Sequance(reversible));   //Accepts ISequenceToken
reversible.Reverse();
print(reversible.Peek());

Could be valid code.

Regardless of how you do it, this level of control and flexibility of how our code runs is fascinating.

I guess the key is 50% = 50/100 = 0.5 (or 200% = 2). Its a mathematical way of thinking and the reason its nice in code is mainly for calculation(10 * 50% = 5). I think its clear there are many and varied solutions to any problem :slight_smile:

Hi, a bit late, but this was very helpful.
I was looking for a way to make characters display a dialog just once, or finite amount of times, without having a lot of trackers inside other classes. Thanks to your tips I was able to make my own class implementing IEnumerator.
Thx a lot!