前言
协程是在我们进行Unity开发的时候常常会使用到的功能,在MonoBehaviour类中,我们可以直接使用StartCoroutine方法来启动一个协程。但是在我们ILRuntime的热更代码中,我们的类不会继承于MonoBehaviour,那么该如何实现呢。
同样的,我们分Unity部分和Hotfix部分来出来
Unity部分
在Unity部分,我们首先需要制作一个MonoBehaviour的单例类MonoBehaviourInstance,这样会方便我们后续的使用。
public class MonoBehaviourInstance<T> : MonoBehaviour
{
public static T instance { get; private set; }
void Awake()
{
if(instance != null)
{
//防止挂载了多个相同的组件
DestroyImmediate(gameObject);
return;
}
instance = GetComponent<T>();
}
}
然后我们新增一个组件IEnumeratorTool继承于上面的单例类,并且将其挂载到场景的GameObject中(若是会切换场景,记得将其设置为DontDestroyOnLoad)
public class IEnumeratorTool : MonoBehaviourInstance<IEnumeratorTool>
{
WaitForSeconds m_waitForOneSecond = new WaitForSeconds(1.0f);
public WaitForSeconds waitForOneSecond
{
get { return m_waitForOneSecond; }
}
}
这样我们在其他地方就可以通过下面方法来启动协程了
IEnumeratorTool.instance.StartCoroutine(IEnumerator routine);
Hotfix部分
当我们用上面的方法,在Hotfix的脚本中启动协程。编译没有问题,但是在运行的时候,ILRuntime会抛出一个异常
Cannot find Adaptor for:System.Collections.Generic.IEnumerator`1
提示很明显,我们缺少了一个Adaptor,我们知道在跨域继承或者实现接口的时候,ILRuntime需要我们自己实现一个Adapter(文档)。个人理解为System.Collections.IEnumerator的方法使用了System.Collections.Generic.IEnumerator<out T>接口。
按着文档的示例,我们新建一个适配器:CoroutineAdapter,同时我们也同步实现System.Collections.IEnumerator和System.IDisposable两个接口的适配。
public class CoroutineAdapter : CrossBindingAdaptor
{
public override Type BaseCLRType
{
get { return null; }
}
public override Type[] BaseCLRTypes
{
get { return new Type[] {typeof(IEnumerator<object>), typeof(IEnumerator), typeof(IDisposable)}; }
}
public override Type AdaptorType
{
get { return typeof(Adaptor); }
}
public override object CreateCLRInstance(ILRuntime.Runtime.Enviorment.AppDomain appdomain,
ILTypeInstance instance)
{
return new Adaptor(appdomain, instance);
}
class Adaptor : IEnumerator<object>, IEnumerator, IDisposable, CrossBindingAdaptorType
{
ILTypeInstance instance;
ILRuntime.Runtime.Enviorment.AppDomain appdomain;
public Adaptor()
{
}
public Adaptor(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
{
this.appdomain = appdomain;
this.instance = instance;
}
public ILTypeInstance ILInstance
{
get { return instance; }
}
IMethod m_moveNextMethod;
bool m_moveNextMethodGot;
public bool MoveNext()
{
if (!m_moveNextMethodGot)
{
m_moveNextMethod = instance.Type.GetMethod("MoveNext", 0);
m_moveNextMethodGot = true;
}
if (m_moveNextMethod != null)
return (bool) appdomain.Invoke(m_moveNextMethod, instance, null);
return false;
}
IMethod m_resetMethod;
bool m_resetMethodGot;
public void Reset()
{
if (!m_resetMethodGot)
{
m_resetMethod = instance.Type.GetMethod("Reset", 0);
m_resetMethodGot = true;
}
if (m_resetMethod != null)
appdomain.Invoke(m_resetMethod, instance, null);
}
IMethod m_getCurrentMethod;
bool m_getCurrentMethodGot;
public object Current
{
get
{
if (!m_getCurrentMethodGot)
{
m_getCurrentMethod = instance.Type.GetMethod("get_Current", 0);
if (m_getCurrentMethod == null)
{
//如果实现的是System.Collections.IEnumerator接口,则用下面的读取方式
m_getCurrentMethod = instance.Type.GetMethod("System.Collections.IEnumerator.get_Current", 0);
}
m_getCurrentMethodGot = true;
}
if (m_getCurrentMethod != null)
return (object) appdomain.Invoke(m_getCurrentMethod, instance, null);
return null;
}
}
IMethod m_disposeMethod;
bool m_disposeMethodGot;
public void Dispose()
{
if (!m_disposeMethodGot)
{
m_disposeMethod = instance.Type.GetMethod("Dispose", 0);
if (m_disposeMethod == null)
{
//如果实现的是System.IDisposable接口,则用下面的读取方式
m_disposeMethod = instance.Type.GetMethod("System.IDisposable.Dispose", 0);
}
m_disposeMethodGot = true;
}
if (m_disposeMethod != null)
appdomain.Invoke(m_disposeMethod, instance, null);
}
}
}
适配器写好后记得要绑定一下
appdomain.RegisterCrossBindingAdaptor(new CoroutineAdapter());
这样我们就可以在Hotfix的代码中,实现协程的使用了。