UFL3-1——Hierarchy窗口布局优化
UFL3-1——Hierarchy窗口布局优化
窗口布局优化指的是什么
现在直接失活对象,当之后项目做大了,抽屉多了,对象多了
游戏中成百上千个对象,在开发测试时不方便从Hierarchy窗口中查看对象获取信息
可见,目前创建出来的对象都是直接排布在场景上而没有做优化,因此我们希望能优化一下Hierarchy窗口中的布局,将对象和抽屉的关系可视化
制作思路和具体实现
-
制作思路:
- 柜子管理自己的柜子根物体
Pool
- 抽屉管理自己的抽屉根物体
- 失活时建立父子关系,激活活时断开父子关系
要实现的效果如下
- 柜子管理自己的柜子根物体
-
具体实现:
-
先实现将所有对象放入柜子根物体中
先声明在缓存池类中声明柜子根物体对象的变量
poolObj
,与场景上的Pool
对象关联起来
当调用PushObj
而柜子根物体Pool
对象不存在于场景的时候,创建这个Pool
对象
然后,所有要压入池子内的对象都作为Pool
对象的子物体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
74using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 缓存池模块 管理器
/// </summary>
public class PoolManager : BaseManager<PoolManager>
{
//柜子容器当中有抽屉的体现
private Dictionary<string, Stack<GameObject>> poolDic = new Dictionary<string, Stack<GameObject>>();
//池子根对象
private GameObject poolObj;
private PoolManager() { }
/// <summary>
/// 拿东西的方法
/// </summary>
/// <param name="name">抽屉容器的名字</param>
/// <returns>从缓存池中取出的对象</returns>
public GameObject GetObj(string name)
{
GameObject obj;
//有抽屉,且抽屉内有对象
if (poolDic.ContainsKey(name) && poolDic[name].Count > 0)
{
obj = poolDic[name].Pop();
obj.SetActive(true);
//取出时,断开父子关系
obj.transform.SetParent(null);
}
//否则,就应该去创建对象
else
{
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
//为了避免实例化出来的对象默认名字带一个"(clone)",我们需要重命名这个对象
obj.name = name;
}
return obj;
}
/// <summary>
/// 往缓存池中放入对象
/// </summary>
/// <param name="name">抽屉(对象池)的名字</param>
/// <param name="obj">要放入的对象</param>
public void PushObj(GameObject obj)
{
//如果根物体为空,就创建
if (poolObj == null)
poolObj = new GameObject("Pool");
//并不是直接移除对象,而是将对象失活一会再用,用的时候再激活它
//除了失活对象,还可以通过把对象放到很远的地方等方式来隐藏对象(失活的性能可能偏低一些)
obj.SetActive(false);
//把失活的对象(要放入抽屉中的对象),如对象
obj.transform.SetParent(poolObj.transform);
//如果不存在对应的抽屉容器,先创建抽屉,再往抽屉内放
if (!poolDic.ContainsKey(obj.name))
poolDic.Add(obj.name, new Stack<GameObject>());
poolDic[obj.name].Push(obj);
}
/// <summary>
/// 清除整个柜子当中的数据
/// </summary>
public void ClearPool()
{
poolDic.Clear();
poolObj = null;
}
}按照之前的测试方法再次测试,得到的Hierarchy窗口布局如下
-
再实现将对象放入对应的抽屉根物体中,用面向对象的思想将抽屉相关数据行为封装起来
新声明一个
PoolData
类,代表具体存储某一种对象的抽屉,该类包括具体的抽屉父对象,存储对象的池子(栈),以及获取池子内对象数量的属性
实例化该类的时候需要传入池子根对象,存储的对象的名字,以方便设置抽屉对象的名字,并将池子根对象对象作为抽屉对象的父对象该类为存储对象的池子(栈)的对象,封装存入
Push
和取出Pop
的方法,存入或取出时要执行一些必备的处理,例如激活,或者父子对象设置等等其中,要存入到池子内的对象,存入前就可以将
PoolData
类对应的抽屉父对象作为自己的父对象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
41public class PoolData
{
//抽屉根对象,用来就行布局管理的对象
private GameObject poolObj;
//用来存储抽屉中的对象
private Stack<GameObject> dataStack = new Stack<GameObject>();
//获取容器中是否有对象
public int Count => dataStack.Count;
public PoolData(GameObject poolRootObj, string poolName)
{
//创建抽屉父对象,和柜子父对象建立父子关系
poolObj = new GameObject(poolName);
poolObj.transform.SetParent(poolRootObj.transform);
}
/// <summary>
/// 从抽屉中弹出数据对象
/// </summary>
/// <returns>弹出的数据对象</returns>
public GameObject Pop()
{
//从抽屉内取出并激活对象,再断开父子关系
GameObject obj = dataStack.Pop();
obj.SetActive(true);
obj.transform.SetParent(null);
return obj;
}
/// <summary>
/// 将数据压入到抽屉内
/// </summary>
/// <param name="obj">压入到抽屉内</param>
public void Push(GameObject obj)
{
//失活后放入将抽屉根对象作为父对象,然后压入到抽屉内
obj.SetActive(false);
obj.transform.SetParent(poolObj.transform);
dataStack.Push(obj);
}
}由于
PoolData
类内封装的存入Push
和取出Pop
的方法已经做好了例如激活,或者父子对象设置等等处理
因此除了PoolManager
对象池字典内的值改为PoolData
类,诸如存入Push
和取出Pop
的方法不再需要实现激活,或者父子对象设置等等逻辑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/// <summary>
/// 缓存池模块 管理器
/// </summary>
public class PoolManager : BaseManager<PoolManager>
{
//柜子容器当中有抽屉的体现
private Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();
//池子根对象
private GameObject poolObj;
private PoolManager() { }
/// <summary>
/// 拿东西的方法
/// </summary>
/// <param name="name">抽屉容器的名字</param>
/// <returns>从缓存池中取出的对象</returns>
public GameObject GetObj(string name)
{
GameObject obj;
//有抽屉,且抽屉内有对象
if (poolDic.ContainsKey(name) && poolDic[name].Count > 0)
{
//弹出栈中的对象,直接返回给外部使用
obj = poolDic[name].Pop();
}
//否则,就应该去创建对象
else
{
obj = GameObject.Instantiate(Resources.Load<GameObject>(name));
//为了避免实例化出来的对象默认名字带一个"(clone)",我们需要重命名这个对象
obj.name = name;
}
return obj;
}
/// <summary>
/// 往缓存池中放入对象
/// </summary>
/// <param name="name">抽屉(对象池)的名字</param>
/// <param name="obj">要放入的对象</param>
public void PushObj(GameObject obj)
{
//如果根物体为空,就创建
if (poolObj == null)
poolObj = new GameObject("Pool");
//如果不存在对应的抽屉容器,先创建抽屉,再往抽屉内放
if (!poolDic.ContainsKey(obj.name))
poolDic.Add(obj.name, new PoolData(poolObj, obj.name));
poolDic[obj.name].Push(obj);
}
/// <summary>
/// 清除整个柜子当中的数据
/// </summary>
public void ClearPool()
{
poolDic.Clear();
poolObj = null;
}
}按照之前的测试方法再次测试,得到的Hierarchy窗口布局如下:
-
将窗口布局优化变为可控制开启功能
频繁变化父子对象带来的性能开销是不可忽视的,而窗口布局优化往往只是为了方便我们在Hierarchy窗口上观察和调试对象
因此,这个窗口布局优化应当是可关闭的,当我们发布游戏或者拥有更佳方案时,该功能就应该关闭掉,让对象池只控制对象的是否激活,提高性能
在PoolManager
内声明一个公开的isOpenLayout
,代表是否开启Hierarchy窗口自动布局功能
如果不开启,则PoolData
内的构造函数,Pop
和Push
,以及PoolManager
的Push
与自动布局相关的逻辑都需要跳过不执行
1 | using System.Collections; |