在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的代码中,实现协程的使用了。

猜你喜欢

转载自blog.csdn.net/wangjiangrong/article/details/104845512