前言
協程是在我們進行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的代碼中,實現協程的使用了。