在ILRuntime中使用協程的簡單方法 StartCoroutine(IEnumerator)

前言

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章