I can, but it’s quite a bit of code, because it hooks into my coroutine system I wrote as well (the coroutine system allows for custom yield statements and canceling of the coroutine).
All the Invoke system does is operate a predefined coroutine that waits a certain amount of time. And it tracks the coroutine so that if you pass in the delegate to a ‘CancelInvoke’ method it cancels that custom coroutine.
Before I wrote my custom coroutine system that wraps around the existing coroutine, I used the Invoke system to do it. So it still had to call by string internally, and it served to just allow passing in a delegate (it was nice for anonymous methods). I did it by merely having a private method with a weird name, stacking the delegates in a list, and calling them as that Invoke came back. I could cancel it by removing it from the list. I prefer this newer coroutine model.
So get ready to scroll.
RadicalCoroutine:
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;
#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; } }
#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 (_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
}
}
IRadicalYieldInstruction (implement this to make a custom yield statement):
using UnityEngine;
namespace com.spacepuppy
{
public interface IRadicalYieldInstruction
{
object CurrentYieldObject { get; }
bool ContinueBlocking(RadicalCoroutine routine);
}
}
SPComponent (where Invoke is implemented, all my component inherit from this):
using UnityEngine;
using System.Collections.Generic;
namespace com.spacepuppy
{
/// <summary>
/// Base contract for any interface contract that should be considered a Component
/// </summary>
public interface IComponent
{
Component component { get; }
GameObject gameObject { get; }
Transform transform { get; }
}
public abstract class SPComponent : MonoBehaviour, IComponent
{
#region Fields
private List<InvokeInfo> _invokeList;
#endregion
#region CONSTRUCTOR
protected virtual void Awake()
{
if (com.spacepuppy.Utils.Assertions.AssertRequireLikeComponentAttrib(this))
{
Object.Destroy(this);
}
}
protected virtual void OnDestroy()
{
if (_invokeList != null)
{
foreach (var info in _invokeList)
{
info.Cancel();
}
_invokeList = null;
}
}
protected virtual void OnEnable()
{
this.SendMessage(SPConstants.MSG_ONSPCOMPONENTENABLED, this, SendMessageOptions.DontRequireReceiver);
}
protected virtual void OnDisable()
{
this.SendMessage(SPConstants.MSG_ONSPCOMPONENTDISABLED, this, SendMessageOptions.DontRequireReceiver);
}
#endregion
#region Invoke
public void Invoke(System.Delegate method, float time, params object[] args)
{
if (method == null) throw new System.ArgumentNullException("method");
if (time <= 0f)
{
method.DynamicInvoke(args);
return;
}
if (_invokeList == null)
{
_invokeList = new List<InvokeInfo>();
}
var info = new InvokeInfo(method, time, args);
var r = RadicalCoroutine.StartCoroutine(this, InvokeCoroutine(info));
info.Radical = r;
_invokeList.Add(info);
}
private System.Collections.IEnumerator InvokeCoroutine(InvokeInfo info)
{
yield return new WaitForSeconds(info.WaitTime);
info.Invoke();
if(_invokeList != null) _invokeList.Remove(info);
}
public new void CancelInvoke()
{
base.CancelInvoke();
if (_invokeList != null)
{
foreach (var info in _invokeList)
{
info.Cancel();
}
_invokeList = null;
}
}
public void CancelInvoke(System.Delegate method)
{
if (_invokeList == null) return;
foreach (var info in _invokeList.ToArray())
{
if (info.Method == method)
{
info.Cancel();
_invokeList.Remove(info);
}
}
}
private class InvokeInfo
{
public float WaitTime;
public System.Delegate Method;
public object[] Args;
public RadicalCoroutine Radical;
public InvokeInfo(System.Delegate m, float t, object[] a)
{
WaitTime = t;
Method = m;
Args = a;
Radical = null;
}
public void Invoke()
{
if (Method != null) Method.DynamicInvoke(Args);
}
public void Cancel()
{
if(Radical != null) Radical.Cancel();
Method = null;
Args = null;
}
}
#endregion
#region Coroutine
public Coroutine StartCoroutine(System.Collections.IEnumerable enumerable)
{
return this.StartCoroutine(enumerable.GetEnumerator());
}
public Coroutine StartCoroutine(System.Delegate method, params object[] args)
{
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 this.StartCoroutine(e);
}
public RadicalCoroutine StartRadicalCoroutine(System.Collections.IEnumerator e)
{
return RadicalCoroutine.StartCoroutine(this, e);
}
public RadicalCoroutine StartRadicalCoroutine(System.Collections.IEnumerable e)
{
return RadicalCoroutine.StartCoroutine(this, e.GetEnumerator());
}
public RadicalCoroutine StartRadicalCoroutine(System.Delegate method, params object[] args)
{
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(this, e);
}
#endregion
#region IComponent Interface
Component IComponent.component
{
get { return this; }
}
//implemented implicitly
/*
GameObject IComponent.gameObject { get { return this.gameObject; } }
Transform IComponent.transform { get { return this.transform; } }
*/
#endregion
}
}
I didn’t look through the code, but it probably references some other classes that I didn’t link here. Probably some utility classes, or custom collections. It’s all part of my SP (space puppy) framework that I’ve been developing for my personal use with unity.
Also, I’ve considered breaking the StartCoroutine methods out as extension methods so you don’t necessarily have to inherit SPComponent for them to work.
Invoke though, since it stores a reference to the list of invoke calls, needs to be here. Otherewise I’d have to write a manager for it that tracked all the components and when they died and what not. I wasn’t a big fan of that, and went this way instead.
Lastly, I may change to use some generics as well. Haven’t really considered how though. But it’d be nice to get compile time checks on the calls.