在ILRuntime里,使用Appdomain.Invoke方法时,最后一个传入参数的地方不能传入参数数组(params object[])。所以我们要修改UI框架的传参方式

修改框架

修改UIManager

1
2
3
public WindowBase CreateWindow(string wndName,bool bTop = true,bool isFromResFolder = false,object param1 = null,object param2 = null,object param3 = null);
public void EnableWnd(string wndName,bool bTop = true, object param1 = null, object param2 = null, object param3 = null);
public void EnableWnd(WindowBase wnd,bool bTop = true, object param1 = null, object param2 = null, object param3 = null);

其余的部分将param1、param2、param3三个参数传进来即可

修改WindowBase

修改WindowBaseOnAwakeOnEnable方法

1
2
public virtual void OnAwake(object param1 = null, object param2 = null, object param3 = null) { }
public virtual void OnEnable(object param1 = null, object param2 = null, object param3 = null) { }

WindowBase适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Enviorment;
using ILRuntime.Runtime.Intepreter;

public class WindowBaseAdaptor : CrossBindingAdaptor
{
public override System.Type BaseCLRType => typeof(WindowBase);

public override System.Type AdaptorType => typeof(Adaptor);

public override object CreateCLRInstance(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
{
return new Adaptor(appdomain,instance);
}
class Adaptor : WindowBase, CrossBindingAdaptorType
{
AppDomain m_AppDomain;
ILTypeInstance m_Instance;
private object[] m_Paralist = new object[3];
IMethod m_OnAwakeMethod;
IMethod m_OnEnableMethod;
IMethod m_OnDisableMethod;
IMethod m_OnUpdateMethod;
IMethod m_OnCloseMethod;
IMethod m_ToStringMethod;
bool m_IsOnCloseInvoking = false;
public Adaptor() { }
public Adaptor(AppDomain appDomain,ILTypeInstance instance)
{
m_Instance = instance;
m_AppDomain = appDomain;
}
public ILTypeInstance ILInstance => m_Instance;

public override void OnAwake(object param1 = null, object param2 = null, object param3 = null)
{
if (m_OnAwakeMethod == null)
{
m_OnAwakeMethod = m_Instance.Type.GetMethod("OnAwake", 3);
}
else
{
m_Paralist[0] = param1;
m_Paralist[1] = param2;
m_Paralist[2] = param3;
m_AppDomain.Invoke(m_OnAwakeMethod, m_Instance, m_Paralist);
}
}
public override void OnEnable(object param1 = null, object param2 = null, object param3 = null)
{
if (m_OnEnableMethod == null)
{
m_OnEnableMethod = m_Instance.Type.GetMethod("OnEnable", 3);
}
else
{
m_Paralist[0] = param1;
m_Paralist[1] = param2;
m_Paralist[2] = param3;
m_AppDomain.Invoke(m_OnEnableMethod, m_Instance, m_Paralist);
}
}
public override void OnDisable()
{
if (m_OnDisableMethod == null)
{
m_OnDisableMethod = m_Instance.Type.GetMethod("OnDisable", 0);
}
else
{
m_AppDomain.Invoke(m_OnDisableMethod, m_Instance);
}
}
public override void OnClose()
{
if (m_OnCloseMethod == null)
{
m_OnCloseMethod = m_Instance.Type.GetMethod("OnClose", 0);
}
if (m_OnCloseMethod != null && !m_IsOnCloseInvoking)
{
m_IsOnCloseInvoking = true;
m_AppDomain.Invoke(m_OnCloseMethod, m_Instance);
m_IsOnCloseInvoking = false;
}
else
{
base.OnClose();
}
}
public override void OnUpdate()
{
if (m_OnUpdateMethod == null)
{
m_OnUpdateMethod = m_Instance.Type.GetMethod("OnUpdate", 0);
}
else
{
m_AppDomain.Invoke(m_OnUpdateMethod, m_Instance);
}
}
public override string ToString()
{
if (m_ToStringMethod == null)
{
m_ToStringMethod = m_AppDomain.ObjectType.GetMethod("ToString", 0);//基础Object通过这种方式获取方法
}
IMethod m = m_Instance.Type.GetVirtualMethod(m_ToStringMethod);
if (m != null || m is ILMethod)//检测ToString方法是否被热更类重载过
{
return m_Instance.ToString();
}
else
{
return m_Instance.Type.FullName;
}

}
}
}

我们在WindowBase基类里面,对OnClose方法有处理:

1
2
3
4
5
6
7
public virtual void OnClose() 
{
RemoveAllButtonListeners();
RemoveAllToggleListeners();
m_AllButton.Clear();
m_AllToggle.Clear();
}

这意味着,WindowBase的子类肯定会调用base.OnClose,而子类只要调用父类,我们的适配器就必须要写类似“m_IsOnCloseInvoking”这样的方法。

我们梳理一下调用逻辑:

UIManager——调用CloseWnd——传到ILRuntime内部的方法——ILRuntime发现是跨域继承的子类——尝试调用WindowBase适配器同名的方法——适配器调用OnClose,并且设置“m_IsOnCloseInvoking”为true——适配器又调用了ILRuntime内部的OnClose方法——内部方法需要调用base.OnClose——再次调用适配器的OnClose,这时由于“m_IsOnCloseInvoking”为true,适配器进行了base.OnClose的调用

修改HotFixWnd

修改HorFixWnd报错的地方

1
public override void OnAwake(object param1 = null, object param2 = null, object param3 = null)

修改HotFix工程内的报错

修改HotFix工程的报错,修改MenuWnd

1
public override void OnAwake(object param1 = null,object param2 = null,object param3 = null)//+++

修改LoadingWnd

1
2
3
4
5
6
public override void OnAwake(object param1 = null, object param2 = null, object param3 = null)//+++
{
m_LoadingPanel = GameObject.GetComponent<LoadingPanel>();
m_SceneName = param1.ToString();//+++
GameSceneManager.Instance.OnSceneLoaded += LoadOtherSceneStartUI;
}

进行CLR绑定

修改主工程的ILRuntimeCLRBinding文件

1
2
3
4
5
static void InitILRuntime(AppDomain domain)
{
//...
domain.RegisterCrossBindingAdaptor(new WindowBaseAdaptor());
}

修改主工程的ILRuntimeManager文件

1
2
3
4
5
6
7
8
void InitializeILRuntime()
{
//...
//跨域继承绑定类注册
//...
m_AppDomain.RegisterCrossBindingAdaptor(new WindowBaseAdaptor());
//...
}

增加委托适配器

我们在WindowBase里面添加过AddButtonClickListener方法,Button.onClick.AddListener方法对应的委托我们还没有注册,所以我们需要添加上。

我们在WindowBase里面添加过OnLoadSpriteFinish回调,代表异步加载完成的回调,我们也需要添加上

ILRuntimeManagerInitializeILRuntime方法中添加新的委托适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//自定义委托注册
m_AppDomain.DelegateManager.RegisterMethodDelegate<System.String, UnityEngine.Object, System.Object, System.Object, System.Object>();
m_AppDomain.DelegateManager.RegisterDelegateConvertor<OnAsyncObjFinish>(action =>
{
return new OnAsyncObjFinish((path, obj,param1,param2,param3) =>
{
((System.Action<System.String, UnityEngine.Object, System.Object, System.Object, System.Object>)action).Invoke(path, obj, param1, param2, param3);
});
});

//UnityAction委托注册
m_AppDomain.DelegateManager.RegisterMethodDelegate<bool>();
m_AppDomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction<bool>>(action =>
{
return new UnityEngine.Events.UnityAction<bool>((a) =>
{
((System.Action<bool>)action).Invoke(a);
});
});
//+++
m_AppDomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>(action =>
{
return new UnityEngine.Events.UnityAction(()=>
{
((System.Action)action).Invoke();
});
});

测试

点击运行,看看是否进入了Menu场景,打开了Menu菜单