UFL8——UI管理器的隐藏面板可选销毁优化

为什么要进行 隐藏面板可选销毁 优化

我们目前隐藏面板时,会直接将面板销毁,下次创建时再重新创建

  • 优点:当存在内存压力时,直接销毁面板后,当内存不足时会触发GC,不会因为存在没有使用的面板引用而造成内存崩溃
  • 缺点:会产生内存垃圾加快GC的触发,频繁的销毁创建会增加性能消耗

也就是说我们不能直接将面板隐藏改成不销毁,而应该改为可以让我们自己控制最好
我们可以根据项目的实际情况 选择性的使用失活或销毁

隐藏面板可选销毁实现

主要制作思路

无需使用缓存池,因为缓存池主要是提供给非唯一对象使用的,UI面板大部分情况下是唯一的,因此我们直接在UI管理器中修改逻辑即可

主要实现内容

  1. 隐藏面板时,可以选择销毁还是失活
  2. 显示面板时,如果存在直接激活,如果不存在再重新创建
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
private Dictionary<string, BasePanelInfo> panelDic = new Dictionary<string, BasePanelInfo>();

public void HidePanel<T>(bool isDestroy = false) where T : BasePanel
{
string panelName = typeof(T).Name;
if (panelDic.ContainsKey(panelName))
{
//取出消息
PanelInfo<T> panelInfo = panelDic[panelName] as PanelInfo<T>;
//如果存在,但正在加载
if (panelInfo.panel == null)
{
//修改隐藏标识,标识这个面板将要隐藏,因为要隐藏,因此将回调置空
panelInfo.isHide = true;
panelInfo.callBack = null;
}
//已经加载结束
else
{
panelInfo.panel.HideMe();
//销毁后从容器中移除
if (isDestroy)
{
GameObject.Destroy(panelInfo.panel.gameObject);
panelDic.Remove(panelName);
}
//如果不销毁,那么就只是失活,下次再显示时
else
panelInfo.panel.gameObject.SetActive(false);
}
}
}

public void ShowPanel<T>(E_UILayer layer = E_UILayer.Middle, UnityAction<T> callBack = null, bool isSync = false) where T : BasePanel
{
//获取面板名,预设体名必须与面板类名一致
string panelName = typeof(T).Name;
//存在面板
if (panelDic.ContainsKey(panelName))
{
//取出消息
PanelInfo<T> panelInfo = panelDic[panelName] as PanelInfo<T>;
//正在异步加载中
if (panelInfo.panel == null)
{
//将回调添加到记录中
panelInfo.isHide = false;
if (callBack != null)
panelInfo.callBack += callBack;
}
//已经加载结束
else
{
//如果是失活状态,直接激活面板,就可以显示了
if (panelInfo.panel.gameObject.activeSelf)
panelInfo.panel.gameObject.SetActive(true);
//如果要显示面板,会执行一次面板的默认显示逻辑
panelInfo.panel.ShowMe();
//直接执行回调,直接传递出去即可
callBack?.Invoke(panelInfo.panel);
}
return;
}

//不存在面板,先存入字典当中占位,之后如果又显示,我才能得到字典中的消息进行判断
panelDic.Add(panelName, new PanelInfo<T>(callBack));
ABResManager.Instance.LoadResAsync<GameObject>("ui", panelName, (res) =>
{
//取出消息
PanelInfo<T> panelInfo = panelDic[panelName] as PanelInfo<T>;
if (panelInfo.isHide)
{
panelDic.Remove(panelName);
return;
}
//层级的处理
Transform layerObj = GetLayerObj(layer);
//避免没有按照指定规则传递参数,避免为空
if (layerObj == null)
layerObj = middleLayer;
//将面板预设体创建到对应父对象下,并且保持原本的缩放大小
GameObject panelObj = GameObject.Instantiate(res, layerObj, false);
T panel = panelObj.GetComponent<T>(); //获取对应的UI控件返回出去
panel.ShowMe(); //显示面板时执行的默认方法
panelInfo.panel = panel; //将加载出来的panel记录到panelInfo内
panelInfo.callBack?.Invoke(panel); //传递到外部使用
panelInfo.callBack = null; //清空回调,避免内存泄露
}, isSync);
}

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
UIManager.Instance.ShowPanel<BeginPanel>(E_UILayer.System, (panel) =>
{
panel.TestFun();
});
}
if (Input.GetKeyDown(KeyCode.H))
{
UIManager.Instance.HidePanel<BeginPanel>();
}
}

按下H键隐藏面板时,面板不会销毁而是隐藏:image