最近实验下assetbundle的热更,本来以为很简单,就创建,上传,下载,加载。但实际上开发的时候才发现个中细节多且复杂。
创建本来以为就一句api就可以了,但是实际操作起来,才发现想的过于简单。
首先,命名,那么多图集,预设,不可能去手动命名,所以要写一个工具。工具原理就是根据文件名,去自动修改文件的assetbundleName以及后缀assetBundleVariant。需要注意的是,这两个参数并不记录在文件本身,而是存在文件的.meta文件里面,文本编辑器打开即可看到。我这边的实现是在project视图选择一个文件,然后去遍历这个文件所在文件夹的文件,根据名字,去给每个文件赋值(我这里所有一个图集都在一个文件夹,并且一个图集的资源都是同名,所以按照这个规则来赋值,规则根据项目实际情况来制定),代码如下:
public static Object[] Objs = new Object[] { };
[MenuItem("Assetbundle/AutoSetAltasBundleNames")]
static void SetAltasBundleNames()
{
Objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
string path = AssetDatabase.GetAssetPath(Objs[0]);
string parentPath = Path.GetDirectoryName(path);
string[] files = Directory.GetFiles(parentPath);
for (int i = 0; i < files.Length; i++)
{
if (Path.GetExtension(files[i]) == ".meta") continue;
string name = Path.GetFileNameWithoutExtension(files[i]);
if (name.EndsWith(".mat") || name.EndsWith("_a") || name.EndsWith("Avatar"))
name = name.Replace(".mat", "").Replace("_a", "").Replace("Avatar", "");
AssetImporter asset = AssetImporter.GetAtPath(files[i]);
asset.assetBundleName = assetBundleLoadManager.assetType.altas.ToString() + "/" + name;
asset.assetBundleVariant = "bundle";
}
}
首先将选中的对象放入objs数组,我这里只选择了一个,然后获取选择的对象路径,获取对象所在文件夹,获取该文件夹下所有对象的路径,遍历,获取筛选后每个对象的AssetImporter,按照规则赋值。只需看关键代码,其他代码因项目因人而异。
命名之后生成bundle,这里选择lz4压缩方式:
[MenuItem("Assetbundle/CreateAssetbundle/LZ4_Compress")]
static void CreateType2()
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.Android);
}
至此,bundle就全部生成到对应文件夹了,但是考虑到后面版本更新时,要去校验那些文件发生了改变,哪些文件需要更新,要更新的文件大小,所以还需要生成一个配置文件,记录所有bundle的大小,名字,后缀,md5值。其中path为ab包生成的目录。
public class assetBundleMsgInfo {
public string bundleName { get; set; }
public long size { get; set; }
public string hash { get; set; }
public string crc { get; set; }
public string BundleVariant { get; set; }
}
//创建文本文件,储存所有bundle信息,用于与服务端文件做对比,然后进行差异更新
[MenuItem("Assetbundle/CreateBundlesMsgFile")]
static void CreateBundlesMsgFile()
{
AssetBundle.UnloadAllAssetBundles(true);
AssetBundle.UnloadAllAssetBundles(false);
List<assetBundleMsgInfo> bundleMsgInfoList = new List<assetBundleMsgInfo>();
AssetBundle asset = AssetBundle.LoadFromFile(Path.Combine(path, "AssetbundleFile"));
AssetBundleManifest maniFest = asset.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
string[] assetArr = maniFest.GetAllAssetBundles();
for (int i = 0; i < assetArr.Length; i++)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
FileStream fs = new FileStream(Path.Combine(path, assetArr[i]), FileMode.Open);
md5.ComputeHash(fs);
long length = fs.Length;
fs.Close();
byte[] hash = md5.Hash;
StringBuilder str = new StringBuilder();
foreach (byte item in hash)
{
str.Append(string.Format("{0:X2}", item));
}
Debug.Log(assetArr[i] + "---" + length + "---" + str);
assetBundleMsgInfo info = new assetBundleMsgInfo();
info.bundleName = assetArr[i];
info.hash = str.ToString();
info.size = length;
info.BundleVariant = "bundle";
bundleMsgInfoList.Add(info);
}
//写入文件
if (File.Exists(Path.Combine(path, "bundleMsg")))
File.Delete(Path.Combine(path, "bundleMsg"));
File.Create(filePath).Close();//创建文件后,关闭文件流,否则写入流无法使用,有异常
//Directory.CreateDirectory(filePath);//文件夹不存在时,需要先创建文件夹
JsonData data = JsonMapper.ToJson(bundleMsgInfoList);
createFile(Path.Combine(path, "bundleMsg"), data.ToString());
AssetDatabase.Refresh();
}
static void createFile(string path, string sb)
{
//不要以utf-8写入,不然json从服务端解析数据时会有异常
using (StreamWriter textWriter = new StreamWriter(path, false))
{
try
{
textWriter.Write(sb);
textWriter.Flush();
textWriter.Close();
Debug.Log("complete!!!!!!");
}
catch (System.Exception e)
{
textWriter.Close();
Debug.LogError(e.Message);
}
}
}
先根据bundle的主manifest文件去获取所有的bundle,然后去遍历这些bundle文件,生成对应的md5值,存起来写入一个文件里面,其中md5的生成是网上搜到的,忘记出处了...其他代码感觉也没什么好解释的,属于一看就懂那种,应该吧...
最后只需创建个版本号文件,创建的步骤就属于完成了,文件内容只需要一个版本号而已。
代码解释没有特别仔细,主要是调用一些现成的api,没有比较复杂的逻辑在里面,所以就没有一句句去解释。json用的litjson。
补充!!!使用的是unity2017.2.1版本,assetbundle的打包会自动实现增量打包,不会全量打包。当时纠结这个纠结了好久。