https://blog.csdn.net/u012740992/article/details/79371986
怎么分析资源的依赖关系呢,并设置AssetBundleName呢?
我们检测资源之间的依赖关系,遍历每一个有引用的资源进行分析,对于非UGUI的图集资源(UGUI图集上面说了),如果此资源A被其他地方资源B引用仅仅1次,那么就将此资源A的AssetBundleName置空不设置,这样打包时,此资源就会自动被和资源B打到一起合成一个AssetBundle包,如此减少打包的碎片。如果资源A被引用超过2次及以上,那么就为资源独立设置AssetBundleName,从而避免被重复打包到几个依赖它的资源包。
这里所说的资源被依赖超过2次就独立打包,如果觉得碎片化太严重,产生太多AssetBundle文件,也可以设置成n(n>=1)次才独立打包,开心就好。
————————————————
版权声明:本文为CSDN博主「漫漫之间n」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012740992/article/details/79371986
前提说明:
本文只是针对Unity5.x及以上版本打包AssetBundle。Unity5.x虽然说打包时会处理好资源的依赖关系,但前提依然要我们设置好目标资源的AssetBundleName,如果设置资源AssetBundleName时忽略了资源之间的依赖关系,那么打包AssetBundle时,依然会产生重复打包的资源,所以我写了一套脚本来自动分析资源的依赖关系,并根据资源的依赖关系来设置AssetBundleName,从而避免不必要的资源重复打包。
我也了解过,
Unity的AssetStore也有对应的图形界面工具,查看和处理打包AssetBundle时遇到的资源重复打包问题,但觉得图形化界面工具还得人工查看和修改,如果项目大了,一千多个ab甚至几千个ab,那么每次更新资源时去查看哪里存在依赖那也挺累得,效率不高,并且手动修正难说说没有看漏改漏。所以,不如来套脚本搞它一把,提高工作效率和质量。
我用的是Unity5.3.6。
前提有点特殊的是Unity的UGUI图集打包,Unity的官网有介绍如何将UGUI图集正确打成AssetBundle(但我现在翻回去找不到链接了= =。),简单说下:
首先,UGUI是有图集的。
我的UGUI图集打包AssetBundle方式是,每张图集打成一个AssetBundle,所以我这里保证了每张UI图片的PackingTag和自己的AssetBundleName一样。原因,如果AssetBundleName不一样,那么这张UI图片后面所打成的AssetBundle包将会扯带了整张图集的内容,这张图集别的UI图片也在用啊,却被无辜包含进了这个ab包。所以通过保证UI图片的PackingTag==AssetBundleName来保证每张图集只存在一个AssetBundle,保证图集不被重复打包。
怎么分析资源的依赖关系呢,并设置AssetBundleName呢?
我们检测资源之间的依赖关系,遍历每一个有引用的资源进行分析,对于非UGUI的图集资源(UGUI图集上面说了),如果此资源A被其他地方资源B引用仅仅1次,那么就将此资源A的AssetBundleName置空不设置,这样打包时,此资源就会自动被和资源B打到一起合成一个AssetBundle包,如此减少打包的碎片。如果资源A被引用超过2次及以上,那么就为资源独立设置AssetBundleName,从而避免被重复打包到几个依赖它的资源包。
这里所说的资源被依赖超过2次就独立打包,如果觉得碎片化太严重,产生太多AssetBundle文件,也可以设置成n(n>=1)次才独立打包,开心就好。
资源依赖处理的代码构建思路:
这里写图片描述
其实资源之间的依赖关系,就是一个树形依赖关系,只要能构建出资源之间的依赖树,那么就能了解到某个资源被多少颗树引用,也就是被多少个资源引用,从而对症下药,自然能合理设置AssetBundleName。
然后有代码LoaderManager.cs和ABInfo.cs。
注意:一定要放在Editor文件夹下!!
LoaderManager.cs
using UnityEngine;
using System.Collections;
using System.IO;
using UnityEditor;
using System.Collections.Generic;
public class LoaderManager {
static Dictionary<string, AssetInfo> assetInfoDict = new Dictionary<string, AssetInfo>();
private static string curRootAsset = string.Empty;
private static float curProgress = 0f;
[MenuItem("AssetBundleMgr / SetAssetbundleName")]
static void SetABNames()
{
string path = GetSelectedAssetPath();
if (path == null)
{
Debug.LogWarning("请先选择目标文件夹");
return;
}
LoaderManager.GetAllAssets(path);
}
[MenuItem("AssetBundleMgr / ClearAllAssetbundelname")]
static void CleaarAllABNames()
{
string[] abnames = AssetDatabase.GetAllAssetBundleNames();
foreach (var n in abnames)
{
AssetDatabase.RemoveAssetBundleName(n, true);
}
}
public static void GetAllAssets(string rootDir) {
assetInfoDict.Clear();
DirectoryInfo dirinfo = new DirectoryInfo(rootDir);
FileInfo[] fs = dirinfo.GetFiles("*.*", SearchOption.AllDirectories);
int ind = 0;
foreach (var f in fs)
{
curProgress = (float)ind / (float)fs.Length;
curRootAsset = "正在分析依赖:"+f.Name;
EditorUtility.DisplayProgressBar(curRootAsset, curRootAsset, curProgress);
ind++;
int index = f.FullName.IndexOf("Assets");
if (index != -1)
{
string assetPath = f.FullName.Substring(index);
Object asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
string upath = AssetDatabase.GetAssetPath(asset);
if (assetInfoDict.ContainsKey(assetPath) == false
&& assetPath.StartsWith("Assets")
&& !(asset is MonoScript)
&& !(asset is LightingDataAsset)
&& asset != null
) {
AssetInfo info = new AssetInfo(upath, true);
//标记一下是文件夹下根资源
CreateDeps(info);
}
EditorUtility.UnloadUnusedAssetsImmediate();
}
EditorUtility.UnloadUnusedAssetsImmediate();
}
EditorUtility.ClearProgressBar();
int setIndex = 0;
foreach (KeyValuePair<string, AssetInfo> kv in assetInfoDict) {
EditorUtility.DisplayProgressBar("正在设置ABName", kv.Key, (float)setIndex/(float)assetInfoDict.Count);
setIndex++;
AssetInfo a = kv.Value;
a.SetAssetBundleName(2);
}
EditorUtility.ClearProgressBar();
EditorUtility.UnloadUnusedAssetsImmediate();
AssetDatabase.SaveAssets();
}
/// <summary>
/// 递归分析每个所被依赖到的资源
/// </summary>
/// <param name="self"></param>
/// <param name="parent"></param>
static void CreateDeps(AssetInfo self, AssetInfo parent = null) {
if (self.HasParent(parent))
return;
if (assetInfoDict.ContainsKey(self.assetPath) == false) {
assetInfoDict.Add(self.assetPath, self);
}
self.AddParent(parent);
Object[] deps = EditorUtility.CollectDependencies(new Object[] { self.GetAsset() });
for (int i = 0; i < deps.Length; i++) {
Object o = deps[i];
if (o is MonoScript || o is LightingDataAsset)
continue;
string path = AssetDatabase.GetAssetPath(o);
if (path == self.assetPath)
continue;
if (path.StartsWith("Assets") == false)
continue;
AssetInfo info = null;
if (assetInfoDict.ContainsKey(path))
{
info = assetInfoDict[path];
}
else {
info = new AssetInfo(path);
assetInfoDict.Add(path, info);
}
EditorUtility.DisplayProgressBar(curRootAsset, path, curProgress);
CreateDeps(info, self);
}
EditorUtility.UnloadUnusedAssetsImmediate();
}
static string GetSelectedAssetPath()
{
var selected = Selection.activeObject;
if (selected == null)
{
return null;
}
Debug.Log(selected.GetType());
if (selected is DefaultAsset)
{
string path = AssetDatabase.GetAssetPath(selected);
Debug.Log("选中路径: " + path);
return path;
}
else
{
return null;
}
}
}
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
ABInfo.cs
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
public class AssetInfo
{
//是不是被打包文件夹下的直接资源
private bool isRootAsset = false;
public string assetPath { get; private set; }
private HashSet<AssetInfo> childSet = new HashSet<AssetInfo>();
private HashSet<AssetInfo> parentSet = new HashSet<AssetInfo>();
public AssetInfo(string assetPath, bool isRootAsset = false)
{
this.assetPath = assetPath;
}
public Object GetAsset()
{
Object asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
return asset;
}
/// <summary>
/// 从这里开始分析构建资源依赖树
/// </summary>
/// <param name="parent"></param>
public void AddParent(AssetInfo parent)
{
if (parent == this || IsParentEarlyDep(parent) || parent == null)
return;
parentSet.Add(parent);
parent.AddChild(this);
parent.RemoveRepeatChildDep(this);
RemoveRepeatParentDep(parent);
}
/// <summary>
/// 清除我父节点对我子节点的重复引用,保证树形结构
/// </summary>
/// <param name="targetParent"></param>
private void RemoveRepeatChildDep(AssetInfo targetChild)
{
List<AssetInfo> infolist = new List<AssetInfo>(parentSet);
for (int i = 0; i < infolist.Count; i++)
{
AssetInfo pinfo = infolist[i];
pinfo.RemoveChild(targetChild);
pinfo.RemoveRepeatChildDep(targetChild);
}
}
/// <summary>
/// 清除我子节点被我父节点的重复引用,保证树形结构
/// </summary>
/// <param name="targetChild"></param>
private void RemoveRepeatParentDep(AssetInfo targetParent)
{
List<AssetInfo> infolist = new List<AssetInfo>(childSet);
for (int i = 0; i < infolist.Count; i++)
{
AssetInfo cinfo = infolist[i];
cinfo.RemoveParent(targetParent);
cinfo.RemoveRepeatParentDep(targetParent);
}
}
private void RemoveChild(AssetInfo targetChild)
{
childSet.Remove(targetChild);
targetChild.parentSet.Remove(this);
}
private void RemoveParent(AssetInfo parent)
{
parent.childSet.Remove(this);
parentSet.Remove(parent);
}
private void AddChild(AssetInfo child)
{
childSet.Add(child);
}
/// <summary>
/// 如果父节点早已当此父节点为父节点
/// </summary>
/// <param name="targetParent"></param>
/// <returns></returns>
private bool IsParentEarlyDep(AssetInfo targetParent)
{
if (parentSet.Contains(targetParent))
{
return true;
}
var e = parentSet.GetEnumerator();
while (e.MoveNext())
{
if (e.Current.IsParentEarlyDep(targetParent))
{
return true;
}
}
return false;
}
public bool HasParent(AssetInfo p)
{
if (parentSet.Contains(p))
return true;
return false;
}
/// <summary>
/// 打包碎片粒度
/// </summary>
/// <param name="pieceThreshold"></param>
public void SetAssetBundleName(int pieceThreshold)
{
AssetImporter ai = AssetImporter.GetAtPath(this.assetPath);
//针对UGUI图集的处理,图集以文件夹为单位打包ab
if (ai is TextureImporter)
{
TextureImporter tai = ai as TextureImporter;
string filePath = System.IO.Path.GetDirectoryName(this.assetPath);
tai.spritePackingTag = filePath.ToLower().Replace("\\", "_").Replace(".png",string.Empty).Replace(".jpg", string.Empty).Replace(" ", string.Empty);
//AssetBundleName和spritePackingTag保持一致
tai.SetAssetBundleNameAndVariant(tai.spritePackingTag + ".ab", null);
Debug.Log("<color=#2E8A00>" + "设置ab,Image资源: " + this.assetPath + "</color>");
}
else
{
string abname = this.assetPath.Replace("/", "_") + ".ab";
//不是图集,而且大于阀值
if (this.parentSet.Count >= pieceThreshold)
{
ai.SetAssetBundleNameAndVariant(abname, string.Empty);
Debug.Log("<color=#6501AB>" + "设置ab,有多个引用: " + this.assetPath+"</color>");
}
//根节点
else if (this.parentSet.Count == 0 || this.isRootAsset)
{
ai.SetAssetBundleNameAndVariant(abname, string.Empty);
Debug.Log("<color=#025082>" + "设置ab,根资源ab: " + this.assetPath + "</color>");
}
else
{
//其余的子资源
ai.SetAssetBundleNameAndVariant(string.Empty, string.Empty);
Debug.Log("<color=#DBAF00>" + "清除ab, 仅有1个引用: " + this.assetPath + "</color>");
}
}
}
}
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
用法:
1,选择Unity内我们所要打包的资源所在的文件夹;
2,菜单栏“AssetBundleMgr->SetAssetbundleName”,完成!看一下资源的AssetBundleName。
如:我选择了Prefabs文件夹,然后菜单栏“AssetBundleMgr->SetAssetbundleName”,资源都被正确设置了AssetBundleName。
————————————————
版权声明:本文为CSDN博主「漫漫之间n」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012740992/article/details/79371986