打包策略

  1. 设置编辑器工具统一设置AB包名及路径管理
  2. 根据依赖关系(自定义依赖关系表)生成不冗余AB包
  3. 基于Asset的全路径生成自己的依赖关系表
  4. 根据自己的依赖关系加载AB包

优点:在编辑器环境下,不需要打包;不会产生冗余AB包;文件夹或文件AB包设置简单方便管理。

整个思路就是Unity自己的Addressable打包方式

缺点:长时间未打包的情况下,用于生成目录所需要的时间比较长。

为什么会产生冗余:

如果我们给一个资产文件打上一个AB包名,给包含此资产的文件夹打上另一个AB包名,那么此资产就会产生冗余

如果我们给一个图片打上一个AB包名,给依赖此图片的另一个Prefab打上另一个AB包名,又会产生冗余??

自定义打包配置SO

基于Assets序列化生成编辑器打包配置SO,表里的设置主要分为两种:

  1. 基于文件夹下所有单个prefab文件进行打包
  2. 基于文件夹进行打包

ABConfig脚本

在Editor文件夹内新建ABConfig脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "ABConfig",menuName ="CreateABConfig")]
public class ABConfig : ScriptableObject
{

// assetbundle Prefab文件所在文件夹路径,会遍历这个文件夹下所有Prefab,
// 所有的Prefab的名字不能重复,必须保证名字的唯一性
public List<string> m_AllPrefabPath = new List<string>();

//指定一个文件夹路径并且指定此文件夹的AssetBundle名
public List<FileDirABName> m_AllFileDirAB = new List<FileDirABName>();

[System.Serializable]
public struct FileDirABName
{
public string ABName;
public string Path;
}
}

这是一个简单的配置表示例,我们可以新建一个ABConfig,然后在“All Prefab Path”里填写所有Prefab文件所在文件夹的Path如:“Assets/Prefabs”;

在“All File Dir AB”里填写文件夹路径和这个文件夹所需要指定的AssetBundle名称(一般都是纯小写),如:

1
2
3
4
5
AB Name: shader
Path: Assets/GameData/Shaders

AB Name: sound
Path: Assets/GameData/Sounds

注意

Prefab文件夹内的各个Prefab名要保证唯一。

生成AB包

  1. 根据单个Prefab文件和整个文件夹设置AB包
  2. 剔除冗余AB包
  3. 生成AB包配置表(xml)

修改Editor文件夹下的BundleEditor脚本:

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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

public class BundleEditor
{
private static string ABCONFIGPATH = "Assets/ResFrame.Editor/Editor/Resource/ABConfig.asset";
private static string BYTEPATH = "Assets/GameData/Data/ABData/AssetBundleConfig.bytes";
private static string m_BundleTargetPath = Application.streamingAssetsPath;
/// <summary>
/// key是包名,value是路径,这是一个包含所有文件夹ab包的DIc
/// </summary>
private static Dictionary<string, string> m_AllFileDic = new Dictionary<string, string>();
/// <summary>
/// 用于过滤,让“PrefabAB包”已经包含的“文件AB包”不参与打包
/// </summary>
private static List<string> m_AllFileAB = new List<string>();
/// <summary>
/// key是Prefab名,value是此prefab依赖的无重复打包资源列表
/// </summary>
private static Dictionary<string, List<string>> m_AllPrefabDic = new Dictionary<string, List<string>>();
/// <summary>
/// 储存所有有效路径,它存储了所有文件夹AB包的路径和Prefab(不是Prefab依赖)的路径,并且不查重
/// </summary>
private static List<string> m_ConfigFil = new List<string>();

[MenuItem("Tools/Build AssetBundle")]
public static void Build()
{
DataEditor.AllXmlToBinary();//所有的xml配置数据转二进制,后期配置表部分用的。
m_AllFileAB.Clear();
m_AllFileDic.Clear();
m_AllPrefabDic.Clear();
m_ConfigFil.Clear();
ABConfig abConfig = AssetDatabase.LoadAssetAtPath<ABConfig>(ABCONFIGPATH);
foreach (ABConfig.FileDirABName fileDir in abConfig.m_AllFileDirAB)//要先设置文件夹AB包,这样后面的Prefab的AB包才能检测到文件夹AB包
{
if(m_AllFileDic.ContainsKey(fileDir.ABName))
{
Debug.LogError("文件夹AB包名重复,请检查!");
}
else
{
m_AllFileDic.Add(fileDir.ABName, fileDir.Path);
m_AllFileAB.Add(fileDir.Path);
m_ConfigFil.Add(fileDir.Path);
}
}

string[] allStr = AssetDatabase.FindAssets("t:Prefab", abConfig.m_AllPrefabPath.ToArray());//根据指定的类型返回GUID

for (int i = 0; i < allStr.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(allStr[i]);
EditorUtility.DisplayProgressBar("查找用于打包的Prefab","Prefab:" + path,i*1.0f/allStr.Length);
m_ConfigFil.Add(path);
if (!ContainAllFileAB(path))
{
GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(path);
//获取当前Prefab的所有引用,查看这些引用是否打包成了AB
string[] allDepend = AssetDatabase.GetDependencies(path);
List<string> allDependPath = new List<string>();
for (int j = 0; j < allDepend.Length; j++)
{
if (!ContainAllFileAB(allDepend[j]) && !allDepend[j].EndsWith(".cs"))//因为引用中也会返回脚本,脚本是无法参与打包的,需要剔除
{
m_AllFileAB.Add(allDepend[j]);
allDependPath.Add(allDepend[j]);
}
}
if(m_AllPrefabDic.ContainsKey(obj.name))
{
Debug.LogError("存在相同名字的Prefab:" + obj.name);
}
else
{
m_AllPrefabDic.Add(obj.name, allDependPath);
}
}
}
foreach (string name in m_AllFileDic.Keys)
{
SetABName(name, m_AllFileDic[name]);//设置文件AB包的名称
}
foreach (string name in m_AllPrefabDic.Keys)
{
SetABName(name, m_AllPrefabDic[name]);//设置PrefabAB包所有依赖项的AB包名称
}

//到此不使用AssetDatabase.SaveAssets()和AssetDatabase.Refresh(),
//这样相当于把文件夹重新加载重新生成meta,是不必要的,我们直接在内存中进行操作就可以
BuildAssetBundle();

string[] oldNames = AssetDatabase.GetAllAssetBundleNames();
for (int i = 0; i < oldNames.Length; i++)
{
AssetDatabase.RemoveAssetBundleName(oldNames[i], true);
EditorUtility.DisplayProgressBar("清除AB包名", "名字:" + oldNames[i],i*1.0f/oldNames.Length);
}

AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.ClearProgressBar();

}
/// <summary>
/// 设置ab包的名称
/// </summary>
/// <param name="name">需要设置的名称</param>
/// <param name="path">此ab包的路径</param>
static void SetABName(string name,string path)
{
AssetImporter assetImporter = AssetImporter.GetAtPath(path);
if (assetImporter != null)
{
assetImporter.assetBundleName = name;
}
else
{
Debug.LogError("不存在此路径文件:" + path);
}
}
/// <summary>
/// 设置多个资源的ab包名称
/// </summary>
/// <param name="name">需要设置的名称</param>
/// <param name="paths">资源路径列表</param>
static void SetABName(string name, List<string> paths)
{
paths.ForEach(path =>
{
SetABName(name, path);
});
}
/// <summary>
/// 查找当前PrefabAB是否已经包含在文件夹AB里面
/// </summary>
static bool ContainAllFileAB(string path)
{
for (int i = 0; i < m_AllFileAB.Count; i++)
{
//后面的Replace是为了防止出现:
//Assets/GameData/Test
//Assets/GameDate/TestTT/a.prefafb
//这样的情况,虽然后者包含前者,但是并不在同一个文件夹
if (path == m_AllFileAB[i] || (path.Contains(m_AllFileAB[i]) && (path.Replace(m_AllFileAB[i], "")[0] == '/')))
{
return true;
}
}
return false;
}
static void BuildAssetBundle()
{
string[] allBundles = AssetDatabase.GetAllAssetBundleNames();
//key是各种Asset的全路径,value是AB包名
Dictionary<string,string> resPathDic = new Dictionary<string,string>();//这一步是为了生成配置表
for (int i = 0; i < allBundles.Length; i++)
{
string[] allBundlePath = AssetDatabase.GetAssetPathsFromAssetBundle(allBundles[i]);//获取到标记为此AB包名的所有资产路径
for (int j = 0; j < allBundlePath.Length; j++)
{
if (allBundlePath[j].EndsWith(".cs"))
{
continue;
}

resPathDic.Add(allBundlePath[j], allBundles[i]);
}
}

DeleteAB();
WriteData(resPathDic);

BuildPipeline.BuildAssetBundles
(
m_BundleTargetPath,
BuildAssetBundleOptions.ChunkBasedCompression,
EditorUserBuildSettings.activeBuildTarget
);

}
/// <summary>
/// 根据全路径AB包字典生成配置表
/// </summary>
/// <param name="resPathDic">全路径AB包字典</param>
static void WriteData(Dictionary<string, string> resPathDic)
{
AssetBundleConfig config = new AssetBundleConfig();
config.ABBasesList = new List<ABBase>();
foreach (string path in resPathDic.Keys)
{
if(!ValidPath(path))
{ continue; }
ABBase aBBase = new ABBase();
aBBase.Path = path;
aBBase.Crc = CRC32.GetCRC32 (path);
aBBase.ABName = resPathDic[path];
aBBase.AssetName = Path.GetFileName (path);
aBBase.ABDependence = new List<string>();
string[] resDependence = AssetDatabase.GetDependencies (path);
for (int i = 0; i < resDependence.Length; i++)
{
string tempPath = resDependence[i];
if (tempPath == path || tempPath.EndsWith(".cs"))//剔除掉自己和脚本
continue;
string abName = "";
if (resPathDic.TryGetValue(tempPath, out abName))//如果此AB包的依赖已经包含在最终要打包的Dic中
{
if (abName == resPathDic[path])//如果此AB包的依赖的AB包名就是它自己,剔除掉
continue;
if (!aBBase.ABDependence.Contains(abName))//如果此AB包依赖的AB包名还没有被添加进去
{
aBBase.ABDependence.Add(abName);
}
}
}
config.ABBasesList.Add(aBBase);
}
//写入xml
string xmlPath = Application.dataPath + "/AssetbundleConfig.xml";
if(File.Exists(xmlPath)) { File.Delete(xmlPath); }
using(FileStream fs = new FileStream(xmlPath,FileMode.Create,FileAccess.ReadWrite,FileShare.ReadWrite))
{
using(StreamWriter sw = new StreamWriter(fs,System.Text.Encoding.UTF8))
{
XmlSerializer xs = new XmlSerializer(typeof(AssetBundleConfig));
xs.Serialize(sw, config);
}
}
//写入二进制
//资源的路径只有在xml配置表中有用,在StreamingAssets里的二进制配置文件中没有意义,只需要用CRC就好了,这里清除掉
foreach (ABBase aBBase in config.ABBasesList)
{
aBBase.Path = string.Empty;
}

using (FileStream fs = new FileStream(BYTEPATH, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, config);
}
AssetDatabase.Refresh();
SetABName("assetbundleconfig", BYTEPATH);
}
/// <summary>
/// 删除已生成的AB包里面无用的AB包
/// </summary>
static void DeleteAB()
{
//如果在打包时修改了之前打包过的AB包的名字,就需要删除无用的AB包
string[] allBundlesName = AssetDatabase.GetAllAssetBundleNames();
DirectoryInfo directory = new DirectoryInfo(m_BundleTargetPath);
FileInfo[] files = directory.GetFiles("*",SearchOption.AllDirectories);
foreach (FileInfo file in files)
{
if (ContainABName(file.Name, allBundlesName) || file.Name.EndsWith(".meta") || file.Name.EndsWith(".manifest") || file.Name.EndsWith("assetbundleconfig"))
{
continue;
}else
{
Debug.Log("此AB包已经被删或者改名了,遂被删除:"+file.Name);
if (File.Exists(file.FullName))
{
File.Delete(file.FullName);
}
if (File.Exists(file.FullName + ".manifest"))
{
File.Delete(file.FullName + ".manifest");
}
}
}
}
/// <summary>
/// 将已经生成的AB包名和将要生成的AB包名比较
/// </summary>
/// <param name="name">已经生成的AB包名</param>
/// <param name="bundleNames">将要生成的AB包名</param>
/// <returns>将要生成的AB包是否已经存在</returns>
static bool ContainABName(string name, string[] bundleNames)
{
for (int i = 0; i < bundleNames.Length; i++)
{
if(name == bundleNames[i])
return true;
}
return false;
}
/// <summary>
/// 是否有效路径,用于剔除掉生成配置表时各种依赖的材质、贴图等的路径信息
/// 因为这些文件并没有被设置成AB包标签
/// </summary>
static bool ValidPath(string path)
{
for (int i = 0; i < m_ConfigFil.Count; i++)
{
if (path.Contains(m_ConfigFil[i]))
{
return true;
}
}
return false;
}

}

文件夹AB包的名称需要自己指定,而PrefabAB包的名称和Prefab的名称一致

PrefabAB包的依赖项中,没有包含在“文件夹AB包”内的依赖项,会设置成和prefab同名的AB包

DeleteAB方法中,如果一个ab包需要删除,它的meta文件我们不主动删除(可以看到我们跳过了),Unity会自动处理meta文件。尽量不手动删除它。

当我们把工程内的资源或文件夹设置了AB包名后,它们对应的meta文件会记录此设置:

1
2
3
4
5
6
7
8
fileFormatVersion: 2
guid: 656279d329887f1459f23ba220fc5467
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName: shaders/*指定了此文件夹的asset bundle名*/
assetBundleVariant:

在实际项目中,使用SVN或GIT会同时更新每个资产对应的meta文件,当我们设置了asset bundle,同时会有大量的meta文件需要更新,但其实这是不必要的,因为我们每次打Build AB包时都会重新设置asset bundle,所以在打包完成后要清除所有的ab包的名称设置(使用AssetDatabase.RemoveAssetBundleName(oldNames[i], true);

打包的基本顺序是:

  1. 先设定文件夹AB包和不重复的PrefabAB包的AB包名称
  2. 根据名称打包,并在打包过程中生成配置表
  3. 删除所有AB包名,保证meta文件不修改

AssetBundleConfig

我们在Script文件夹中新建一个AssetBundleConfig类,用于在打包时生成xml配置文件和二进制配置文件

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
using System.Collections.Generic;
using System.Xml.Serialization;

[System.Serializable]
public class AssetBundleConfig
{
[XmlElement("ABList")]
public List<ABBase> ABBasesList { get; set; }
}
[System.Serializable]
public class ABBase
{
/// <summary>
/// 此AB包的全路径
/// </summary>
[XmlAttribute("Path")]
public string Path { get; set; }
/// <summary>
/// 此AB包的唯一标识
/// </summary>
[XmlAttribute("Crc")]
public uint Crc { get; set; }
/// <summary>
/// 此AB包的名字
/// </summary>
[XmlAttribute("ABName")]
public string ABName { get; set; }
/// <summary>
/// 此AB包内部的资产名
/// </summary>
[XmlAttribute("AssetName")]
public string AssetName { get; set; }
/// <summary>
/// 此AB包的依赖
/// </summary>
[XmlElement("ABDependence")]
public List<string> ABDependence { get; set; }
}

我们在打包的过程中生成配置文件,分为两种,一种是xml配置文件,方便在编辑器环境下查看,一种是二进制配置文件,用于传输和运行时使用。

我们在生成二进制配置文件时,将二进制配置文件生成在需要打AB包的文件夹内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static string bytePath = "Assets/GameData/Data/ABData/AssetBundleConfig.bytes";//此为AB包文件夹        
//...
//写入二进制
//资源的路径只有在xml配置表中有用,在StreamingAssets里的二进制配置文件中没有意义,只需要用CRC就好了,这里清除掉
foreach (ABBase aBBase in config.ABBasesList)
{
aBBase.Path = string.Empty;
}

using (FileStream fs = new FileStream(bytePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, config);
}

注意,在一开始”Assets/GameData/Data/ABData/”中是没有文件的,我们需要随便加点AssetBundleConfig.bytes同名文件进去,否则Unity可能不会给空文件夹打AB包名

CRC32

我们在Script文件夹新建个CRC32类,这是一个工具类,用于生成crc,为了快速在内存中定位AB包

CRC对内存更加友好,也更加高效。所以使用CRC标记

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


public class CRC32
{
static UInt32[] crcTable =
{
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};

public static uint GetCRC32(string msg)
{
byte[] bytes = Encoding.UTF8.GetBytes(msg);
int iCount = bytes.Length;
uint crc = 0xFFFFFFFF;

for (uint i = 0; i < iCount; i++)
{
crc = ((crc >> 8) & 0x00FFFFFF) ^crcTable[(crc ^ bytes[i]) & 0xFF];
}

return crc ^ 0xffffffff;
}
}

简单读取AB包

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
void TestLoadAB()
{
//从AB包中读取二进制配置文件
AssetBundle configAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/assetbundleconfig");
TextAsset textAsset = configAB.LoadAsset<TextAsset>("AssetBundleConfig");
using (MemoryStream ms = new MemoryStream(textAsset.bytes))
{
BinaryFormatter bf = new BinaryFormatter();
//首先读取整个Asset Bundle的二进制配置文件
AssetBundleConfig result = (AssetBundleConfig)bf.Deserialize(ms);
//得到想要读取文件的CRC标识
string path = "Assets/Prefabs/Attack.prefab";
uint crc = CRC32.GetCRC32(path);
//从Asset Bundle配置文件中获取到ABBase,二进制配置文件中只能用CRC获取ABBase
ABBase abBase = null;
for (int i = 0; i < result.ABBasesList.Count; i++)
{
if (result.ABBasesList[i].Crc == crc)
{
abBase = result.ABBasesList[i];
}
}
for (int i = 0; i < abBase.ABDependence.Count; i++)
{
AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + abBase.ABDependence[i]);
}
AssetBundle assetbundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + abBase.ABName);
GameObject objPrefab = assetbundle.LoadAsset<GameObject>(abBase.AssetName);
Instantiate(objPrefab);
}
}