什么是离线数据

当我们使用对象池时,如果我们修改了从对象池取出来的对象(大部分情况下,我们会对Prefab做这样的操作),我们会在放回对象池时还原它或者再从对象池取出时还原它。每次手动还原都很麻烦,所以我们可以针对每个Prefab变化的点提前做好“离线数据”,当对象池每次重置Prefab时使用这个数据就好了。

离线数据的分类

离线数据可以灵活分类,这里我们将其分为UI离线数据、特效离线数据、普通离线数据三类

离线数据基类

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
using UnityEngine;

public class OfflineData : MonoBehaviour
{
public Rigidbody m_RigidBody;
public Collider m_Collider;
public Transform[] m_AllPoint;
public int[] m_AllPointChildCount;
public bool[] m_AllPointActive;
public Vector3[] m_Pos, m_Scale;
public Quaternion[] m_Rot;

/// <summary>
/// 还原属性
/// </summary>
public virtual void ResetProp()
{
int allPointCount = m_AllPoint.Length;
for (int i = 0; i < allPointCount; i++)
{
Transform tempTrans = m_AllPoint[i];
if (tempTrans != null)
{
tempTrans.localPosition = m_Pos[i];
tempTrans.localRotation = m_Rot[i];
tempTrans.localScale = m_Scale[i];

if (m_AllPointActive[i])
{
if (!tempTrans.gameObject.activeSelf)
{
tempTrans.gameObject.SetActive(true);
}
}
else
{
if (tempTrans.gameObject.activeSelf)
{
tempTrans.gameObject.SetActive(false);
}
}

if (tempTrans.childCount > m_AllPointChildCount[i])
{
int childCount = tempTrans.childCount;
for (int j = m_AllPointChildCount[i]; j < childCount; j++)
{
GameObject tempObj = tempTrans.GetChild(j).gameObject;
if (!ObjectPoolManager.Instance.IsPoolManagerCreat(tempObj))
{
GameObject.Destroy(tempObj);
}
}
}
}
}
}
/// <summary>
/// 初始属性,在编辑器环境下赋值好
/// </summary>
public virtual void BindData()
{
m_Collider = gameObject.GetComponentInChildren<Collider>(true);
m_RigidBody = gameObject.GetComponentInChildren<Rigidbody>(true);
m_AllPoint = gameObject.GetComponentsInChildren<Transform>(true);
int allPointCount = m_AllPoint.Length;
m_AllPointChildCount = new int[allPointCount];
m_AllPointActive = new bool[allPointCount];
m_Pos = new Vector3[allPointCount];
m_Rot = new Quaternion[allPointCount];
m_Scale = new Vector3[allPointCount];
for (int i = 0; i < allPointCount; i++)
{
Transform temp = m_AllPoint[i] as Transform;
m_AllPointChildCount[i] = temp.childCount;
m_AllPointActive[i] = temp.gameObject.activeSelf;
m_Pos[i] = temp.localPosition;
m_Rot[i] = temp.localRotation;
m_Scale[i] = temp.localScale;
}
}
}

离线数据编辑器代码

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
using UnityEditor;
using UnityEngine;

public class OfflineDataEditor
{
[MenuItem("Assets/离线数据/生成离线数据")]
public static void AssetCreateOfflineData()
{
GameObject[] objects = Selection.gameObjects;
for (int i = 0; i < objects.Length; i++)
{
EditorUtility.DisplayProgressBar("添加离线数据", "正在修改:" + objects[i] + "……", 1.0f / objects.Length * i);
CreateOfflineData(objects[i]);
}
EditorUtility.ClearProgressBar();
}
public static void CreateOfflineData(GameObject obj)
{
OfflineData offlieData = obj.GetComponent<OfflineData>();
if (offlieData == null)
{
offlieData = obj.AddComponent<OfflineData>();
}
offlieData.BindData();
EditorUtility.SetDirty(obj);
Debug.Log("修改了" + obj.name + " prefab");
Resources.UnloadUnusedAssets();
AssetDatabase.Refresh();
}
}

离线数据和对象池结合

修改ResourceObj中间类

1
2
3
4
5
6
7
8
9
10
11
public class ResourceObj
{
//...
//离线数据
public OfflineData m_OfflineData = null;
public void Reset()
{
//...
m_OfflineData = null;
}
}

修改Object Pool Manager的InstantiateObject方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   public GameObject InstantiateObject(string path,bool setSceneObj = false, bool bClear = true)
{
uint crc = CRC32.GetCRC32(path);
ResourceObj resourceObj = GetObjectFromDicCache(crc);
if (resourceObj == null)
{
//...
if(resourceObj.m_ResItem.m_Obj != null)
{
resourceObj.m_CloneObj = GameObject.Instantiate(resourceObj.m_ResItem.m_Obj) as GameObject;
resourceObj.m_OfflineData = resourceObj.m_CloneObj.GetComponent<OfflineData>();//+++
}
}
//...
}

修改Object Pool Manager的OnLoadResourceObjFinish方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    void OnLoadResourceObjFinish(string path, ResourceObj resObj, object param1 = null, object param2 = null, object param3 = null)
{
if (resObj == null)
{
return;
}
if(resObj.m_ResItem.m_Obj == null)
{
#if UNITY_EDITOR
Debug.LogError("异步加载的资源为空:" + path);
#endif
}
else
{
resObj.m_CloneObj = GameObject.Instantiate(resObj.m_ResItem.m_Obj) as GameObject;
resObj.m_OfflineData = resObj.m_CloneObj.GetComponent<OfflineData>();//+++
}
}

离线数据通过从对象池获取的时候来赋值,因为我们放进对象池内的对象不一定全都取出来,所以不需要放进对象池的时候使用离线数据

修改Object Pool Manager的GetObjectFromDicCache方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    protected ResourceObj GetObjectFromDicCache(uint crc)
{
List<ResourceObj> resObjList = null;
if (m_ObjectPoolDic.TryGetValue(crc, out resObjList) && resObjList != null && resObjList.Count>0)
{
//...
if(!System.Object.ReferenceEquals(obj, null))//更高效的判空方法
{
if(!System.Object.ReferenceEquals(resObj.m_OfflineData, null))//+++
{
resObj.m_OfflineData.ResetProp();
}
resObj.m_AlreadyReleased = false;
//...
}
return resObj;
}
return null;
}

Object Pool Manager添加FindOfflineData方法

添加此方法可以让我们很方便地修改一个GameObject的数据信息,比如它的Transform、Animator等,我们不需要再使用GetComponentTransform.Find等方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// <summary>
/// 根据实例化对象直接获取离线数据
/// </summary>
/// <param name="obj">已经实例化的对象</param>
/// <returns>此对象的离线数据</returns>
public OfflineData FindOfflineData(GameObject obj)
{
OfflineData data = null;
ResourceObj resourceObj = null;
m_ResourceObjDic.TryGetValue(obj.GetInstanceID(), out resourceObj);
if (resourceObj != null)
{
data = resourceObj.m_OfflineData;
}
return data;
}

离线数据注意事项

离线数据还原时,一个父节点下多余的子节点会被删除,请注意子节点之间的顺序不要调换,如果在使用Prefab时调换了顺序,需要手动还原,离线数据并不记录节点顺序。