本篇主要是分享unity Assetbundle的打包处理。
目录
打包接口
整体设计
打包AssetBundle
压缩资源到StreamingAssets、输出资源清单文件
清单文件结构
AssetFile:
AssetVersion:
处理:
完整代码
BuildPipeline.BuildAssetBundles(folder, BuildAssetBundleOptions.UncompressedAssetBundle, platform);
打包AssetBundles主要是通过Unity Editor提供的BuildPipeline.BuildAssetBundles这个接口来完成打包。
folder就是打包好之后的输出路径,platform根据要输出的平台选择即可。
BuildAssetBundleOptions里unity提供了一些配置可以帮助我们快速处理一些需求比如压缩等。这个在unity手册里有很详细的解释。
这里选择不压缩主要是为了后续自己做压缩和加载方案。
这里打包分成了四个步骤。
其中拷贝压缩后的StreamingAssets是因为在打安卓或者ios的包之后StreamingAssets路径下的资源才会被放入包体之中供应用使用。
另外,这里我们使用unity打包接口时使用了无压缩的方式是为了后续我们自己做一次压缩。
unity提供的LZ4压缩方案大概会将原始的Assetbundle压缩到1/3左右(实际测试50m=>15m)。
如果我们用一些其他压缩方式可以让文件压缩率更高,比如LZMA可以压缩到1/5左右(实际测试50m=>9m)
这个地方关于打包选择如何压缩应该结合实际项目来考虑,压缩率高的方式解压和压缩花费的时间是更长的。这里个人采用的方案是首次进入游戏前将压缩的文件做一次性的解压,后续玩家进入游戏后加载资源包就不需要再解压。如果项目完全不考虑包体大小的情况则可以完全不压缩。如果项目不希望玩家首次进游戏有解压处理(考虑到一些玩家会有不耐烦的特性),但是游戏包体又不能太大则可以使用Unity提供的LZ4的压缩方案,该方案在调用UnityAssetBundle加载接口加载资源包里的资源的时候会自动的解压(这样首次加载某个游戏资源的时候会略微慢一点,但只要对应的AssetBundle不卸载,再次加载相同资源就会很快。这是因为unityAssetBundle加载过一次后会缓存到内存中,只要AssetBundle不卸载后续就不需要再次从硬盘读取并解压了)。如果对包体有极致的追求则可以考虑我现在这种方案,自己定制压缩,首次进入游戏做一次性的解压。
生成bundles清单文件主要是为了记录当前玩家游戏资源的相关信息,用于和服务器做对比以便于将最新的资源文件从服务器更新下来。
// 设置资源包输出路径
string folder = PathMgr.instance.BuildPath;
// 设置平台
BuildTarget platform = BuildTarget.Android;
#if UNITY_ANDROID
platform = BuildTarget.Android;
#elif UNITY_IOS
platform = BuildTarget.iOS;
#endif
// 调用打包接口
BuildPipeline.BuildAssetBundles(folder, BuildAssetBundleOptions.ChunkBasedCompression, platform);
这里打包输出的资源仅仅是原始资源并不是最终使用的资源,为了避免和游戏资源混淆所以将输出路径设置在了Assets文件夹平级的一个自定义文件夹里面。
// 文件的信息
public class AssetFile
{
// 文件名
public string fileName;
// 文件大小
public int fileSize;
// md5
public string md5;
}
// 资源清单结构
public class AssetVersion
{
// 所有资源文件
public List files;
// 版本号
public int version;
// 下载路径
public string downPath;
}
fileSize属性主要是为了方便后续热更的时候展示更新文件的进度。
md5码有两个用处:
1.将本地的版本文件md5和服务器对应文件的md5做比较,确定该文件是否需要更新。
2.文件更新完成后将文件生成一次md5和服务器md5对比最终确定文件正确更新下来了。(这么做是因为有可能存在网络传输异常,或者服务器的资源文件放错了之类的情况)
记录了所有资源单个文件信息
记录了资源版本号(热更新之前优先比较版本号,版本号低于服务器版本才进行热更。这样可以避免每次都去对比md5节省处理时间。)
记录了资源下载路径(后续热更的时候将该路径和文件名传递给服务器来下载对应的文件)
// 设置压缩资源存放路径
string zipFolder = PathMgr.instance.BuildPath + "Zip/";
// 遍历所有的文件调用压缩方法//顺便生成版本文件
AssetVersion version = new AssetVersion();
version.version = 1;
version.downPath = "xxx/xxx";
version.files = new List();
int idx = 0;
string[] files = Directory.GetFiles(folder, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
string fileName = Path.GetFileName(file);
string destFileName = zipFolder + fileName;
idx++;
EditorUtility.DisplayProgressBar("资源打包", "正在压缩" + fileName + "并生成版本文件", (float)idx / files.Length * 0.5f);
// 压缩
FileTool.CompressFileLZMA(file, destFileName);
// 获取文件信息
FileInfo fi = new FileInfo(destFileName);
AssetFile assetFile = new AssetFile();
assetFile.fileName = fileName;
assetFile.fileSize = (int)fi.Length;
assetFile.md5 = MD5.GetMD5FromFile(destFileName);
version.files.Add(assetFile);
// 复制压缩后的文件到StreamingAsset
File.Copy(destFileName, Application.streamingAssetsPath + "/" + fileName);
}
EditorUtility.DisplayProgressBar("资源打包", "全部压缩完毕,正在保存版本文件", 1f);
// 将版本文件写入到StreamingAsset和压缩文件夹
DataWriteRead.SaveJson(Application.streamingAssetsPath + "/version", version);
DataWriteRead.SaveJson(zipFolder + "/version", version);
public class BuildAssetBundle
{
[MenuItem("Tool/Build")]
private static void BuildPackAsset()
{
try
{
BuildTarget platform = BuildTarget.Android;
#if UNITY_ANDROID
platform = BuildTarget.Android;
#elif UNITY_IOS
platform = BuildTarget.iOS;
#endif
// 打包资源到包体路径
string folder = PathMgr.instance.BuildPath;
FileTool.TruncateDirectory(folder);
FileTool.TruncateDirectory(Application.streamingAssetsPath);
EditorUtility.DisplayProgressBar("资源打包", "开始打包AssetBundle", 0.1f);
BuildPipeline.BuildAssetBundles(folder, BuildAssetBundleOptions.ChunkBasedCompression, platform);
AssetDatabase.Refresh();
EditorUtility.DisplayProgressBar("资源打包", "打包AssetBundle完成", 0.4f);
// 压缩资源包
string zipFolder = PathMgr.instance.BuildPath + "Zip/";
FileTool.TruncateDirectory(zipFolder);
// 构架版本清单文件
AssetVersion version = new AssetVersion();
version.version = 1;
version.downPath = "xxx/xxx";
version.files = new List();
// 压缩所有文件并记录信息
int idx = 0;
string[] files = Directory.GetFiles(folder, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
string fileName = Path.GetFileName(file);
string destFileName = zipFolder + fileName;
idx++;
EditorUtility.DisplayProgressBar("资源打包", "正在压缩" + fileName + "并生成版本文件", (float)idx / files.Length * 0.5f);
// 压缩
FileTool.CompressFileLZMA(file, destFileName);
// 获取文件信息
FileInfo fi = new FileInfo(destFileName);
AssetFile assetFile = new AssetFile();
assetFile.fileName = fileName;
assetFile.fileSize = (int)fi.Length;
assetFile.md5 = MD5.GetMD5FromFile(destFileName);
version.files.Add(assetFile);
// 复制压缩后的文件到StreamingAsset
File.Copy(destFileName, Application.streamingAssetsPath + "/" + fileName);
}
EditorUtility.DisplayProgressBar("资源打包", "全部压缩完毕,正在保存版本文件", 1f);
// 将版本文件写入到StreamingAsset和压缩文件夹
DataWriteRead.SaveJson(Application.streamingAssetsPath + "/version", version);
DataWriteRead.SaveJson(zipFolder + "/version", version);
}
finally
{
EditorUtility.ClearProgressBar();
}
}
}