This is what I use, I wrote my own Coroutine system, I called it RadicalCoroutine (I once read someone refer to custom coroutines as radicalcoroutines, I liked the name… anyone who feels I ripped off the name, this code isn’t intended to be distributed to the public, just sharing my internal code). I wanted a few things from my coroutines.
I wanted them to be cancellable.
I wanted them to be pausable.
I wanted to be able to define my own yield statements.
I wanted them to operate on the same backbone that coroutines do (threads not an option).
I wanted them to allow nesting of coroutines (just like in coroutines, if you yield an enumerable, it enumerates it before continueing)
I wanted them to operate directly on the object that you operated them (no need for special monobehaviour, or global gameobject)
This is the result:
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
namespace com.spacepuppy
{
public class RadicalCoroutine : IRadicalYieldInstruction, System.Collections.IEnumerator
{
#region Fields
private Coroutine _coroutine;
private System.Collections.IEnumerator _routine;
private object _current;
private DerivativeOperation _derivative;
private bool _cancelled = false;
private bool _paused = false;
#endregion
#region CONSTRUCTOR
private RadicalCoroutine(System.Collections.IEnumerator routine)
{
_routine = routine;
}
private void SetOwner(Coroutine co)
{
_coroutine = co;
}
#endregion
#region Properties
/// <summary>
/// A refernence to the routine that is operating this RadicalCoroutine.
/// This can be used for yielding this RadicalCoroutine in the midst of
/// a standard coroutine.
/// </summary>
public Coroutine Coroutine { get { return _coroutine; } }
public bool Cancelled { get { return _cancelled; } }
public bool Paused
{
get { return _paused; }
set { _paused = value; }
}
#endregion
#region Methods
public void Cancel()
{
_cancelled = true;
}
public void Reset()
{
_current = null;
_derivative = null;
_cancelled = false;
_routine.Reset();
}
private bool MoveNext()
{
_current = null;
if (_paused) return true;
if (_cancelled)
{
_derivative = null;
return false;
}
int derivativeResult = -1;
derivativeResult = this.OperateDerivative();
if (derivativeResult >= 0)
{
return (derivativeResult > 0);
}
while (_routine.MoveNext())
{
var obj = _routine.Current;
if (obj is IRadicalYieldInstruction)
{
_derivative = new DerivativeOperation(obj as IRadicalYieldInstruction);
derivativeResult = this.OperateDerivative();
if (derivativeResult >= 0)
{
return (derivativeResult > 0);
}
}
else if (obj is System.Collections.IEnumerable)
{
_derivative = new DerivativeOperation(new RadicalCoroutine((obj as System.Collections.IEnumerable).GetEnumerator()));
derivativeResult = this.OperateDerivative();
if (derivativeResult >= 0)
{
return (derivativeResult > 0);
}
}
else if (obj is System.Collections.IEnumerator)
{
_derivative = new DerivativeOperation(new RadicalCoroutine(obj as System.Collections.IEnumerator));
derivativeResult = this.OperateDerivative();
if (derivativeResult >= 0)
{
return (derivativeResult > 0);
}
}
else
{
_current = obj;
return true;
}
}
_current = null;
_derivative = null;
return false;
}
/// <summary>
/// 1 - continue blocking
/// 0 - stop blocking
/// -1 - loop past
/// </summary>
/// <returns></returns>
private int OperateDerivative()
{
if (_derivative == null) return -1;
if (_derivative.ContinueBlocking(this))
{
if (_cancelled)
{
_current = null;
return 0;
}
_current = _derivative.CurrentYieldObject;
return 1;
}
else
{
_derivative = null;
if (_cancelled)
{
_current = null;
return 0;
}
_current = null;
return -1;
}
}
#endregion
#region IRadicalYieldInstruction Interface
object IRadicalYieldInstruction.CurrentYieldObject
{
get { return _current; }
}
bool IRadicalYieldInstruction.ContinueBlocking(RadicalCoroutine routine)
{
return this.MoveNext();
}
#endregion
#region IEnumerator Interface
object System.Collections.IEnumerator.Current
{
get { return _current; }
}
bool System.Collections.IEnumerator.MoveNext()
{
return this.MoveNext();
}
void System.Collections.IEnumerator.Reset()
{
this.Reset();
}
#endregion
#region Factory Methods
public static RadicalCoroutine StartCoroutine(MonoBehaviour behaviour, System.Collections.IEnumerator routine)
{
if (behaviour == null) throw new System.ArgumentNullException("behaviour");
if (routine == null) throw new System.ArgumentNullException("routine");
var co = new RadicalCoroutine(routine);
co.SetOwner(behaviour.StartCoroutine(co));
return co;
}
#endregion
#region Special Types
private class DerivativeOperation : IRadicalYieldInstruction
{
private IRadicalYieldInstruction _instruction;
private DerivativeOperation _derivative;
public DerivativeOperation(IRadicalYieldInstruction instruction)
{
if (instruction == null) throw new System.ArgumentNullException("instruction");
_instruction = instruction;
}
public object CurrentYieldObject
{
get { return (_derivative != null) ? _derivative.CurrentYieldObject : _instruction.CurrentYieldObject; }
}
public bool ContinueBlocking(RadicalCoroutine routine)
{
if (_derivative != null)
{
if (_derivative.ContinueBlocking(routine))
{
return true;
}
else
{
_derivative = null;
}
}
while (_instruction.ContinueBlocking(routine))
{
if (_instruction.CurrentYieldObject is IRadicalYieldInstruction)
{
_derivative = new DerivativeOperation(_instruction.CurrentYieldObject as IRadicalYieldInstruction);
if (!_derivative.ContinueBlocking(routine))
{
_derivative = null;
}
else
{
return true;
}
}
else if (_instruction.CurrentYieldObject is System.Collections.IEnumerable)
{
_derivative = new DerivativeOperation(new RadicalCoroutine((_instruction.CurrentYieldObject as System.Collections.IEnumerable).GetEnumerator()));
if (!_derivative.ContinueBlocking(routine))
{
_derivative = null;
}
else
{
return true;
}
}
else if (_instruction.CurrentYieldObject is System.Collections.IEnumerator)
{
_derivative = new DerivativeOperation(new RadicalCoroutine(_instruction.CurrentYieldObject as System.Collections.IEnumerator));
if (!_derivative.ContinueBlocking(routine))
{
_derivative = null;
}
else
{
return true;
}
}
else
{
return true;
}
}
return false;
}
}
#endregion
}
}
And this is the interface of the IRadicalYieldInstruction:
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
namespace com.spacepuppy
{
public interface IRadicalYieldInstruction
{
object CurrentYieldObject { get; }
bool ContinueBlocking(RadicalCoroutine routine);
}
}
And here’s some static extension methods for calling them directly off the object, so you can say something like:
//in a MonoBehaviour
var routine = this.StartRadicalCoroutine(this.SomeMethod());
routine.Cancel();//cancels the routine
//the static methods
public static RadicalCoroutine StartRadicalCoroutine(this MonoBehaviour behaviour, System.Collections.IEnumerator e)
{
return RadicalCoroutine.StartCoroutine(behaviour, e);
}
public static RadicalCoroutine StartRadicalCoroutine(this MonoBehaviour behaviour, System.Collections.IEnumerable e)
{
return RadicalCoroutine.StartCoroutine(behaviour, e.GetEnumerator());
}
public static RadicalCoroutine StartRadicalCoroutine(this MonoBehaviour behaviour, System.Delegate method, params object[] args)
{
if (behaviour == null) throw new System.ArgumentNullException("behaviour");
if (method == null) throw new System.ArgumentNullException("method");
System.Collections.IEnumerator e;
if (com.spacepuppy.Utils.ObjUtil.IsType(method.Method.ReturnType, typeof(System.Collections.IEnumerable)))
{
e = (method.DynamicInvoke(args) as System.Collections.IEnumerable).GetEnumerator();
}
else if (com.spacepuppy.Utils.ObjUtil.IsType(method.Method.ReturnType, typeof(System.Collections.IEnumerator)))
{
e = (method.DynamicInvoke(args) as System.Collections.IEnumerator);
}
else
{
throw new System.ArgumentException("Delegate must have a return type of IEnumerable or IEnumerator.", "method");
}
return RadicalCoroutine.StartCoroutine(behaviour, e);
}