BuildAssetBundle 读一读 看看热闹
目录
打包是干什么的?
在Unity3D开发的游戏中,无论模型,音频,还是图片等,我们都做成Prefab,然后打包成Assetbundle,方便我们后面的使用,来达到资源的更新。
====
In Our Program:
版本机上打版本的时候会build资源,同步build热更新包
在MLDJCommand.cs下加入了一个菜单项
BuildAssetBundle一共分为7个Step:
Step 1:
按照资源类型,顺序打包,这里其实是按照资源分类分别封装函数,具体要打成多少个assetbundle,是在每一个_Internal函数内部进行控制。
一个最基本的原则:想通的资源尽可能的打包到一起,他们共用一套资源。不相同的模型尽量分开打包。
总的来说是按照文件夹来build的。
举例:
BuildModelAssetBundle("/DJMain/BundleData/Model", "BundleData/Model",
"Assets/StreamingAssets" + BundleManager.PathModelPrefab, target, modelFileEnd);
参数是路径信息,最后modelFileEnd是后缀名
比如BuildModel的时候就是loadPath = "Assets/StreamingAssets/Model"
其实就是我们要build的资源所在的Unity的原路径
然后通过BuildDependencyAssetBundle打带有依赖的AssetBundle包
- 检查路径有效性
CheckTargetPath函数中回对这个目标路径做一些格式的转换,确保路径的存在工作
- 遍历所有prefab计算依赖项
通过调用BundleMaker.cs中的GetAssetInPath函数,遍历整个文件夹,获得两个字典
一个是singlePrefabs<路径,文件名>字典 (1110个)
一个是singleObjsDic<文件名,Object>字典
第二个字典是通过Unity API AssetDatabase.LoadMainAssetAtPath获取的assets文件夹下的所有特定类型的物体
然后把这些Object放入curLoadPrefab数组,用来计算依赖项
然后通过Unity Api EditorUtility.CollectDependencies 来计算curLoadPrefab里面的所有依赖资源 dependObjs
这一步的时间还是比较长的
- 计算 共享资源包
在这些dependObjs中,对于所有的UnityEngine.Shader类型的资源,如果是在"Assets/DJMain"下面,则加入shaderDependObjs里面,用来打一个共享包
然后把CommonEffectTexture和CommonTexture所用到的texture加到shaderDependObjs里面,通过名字能看出来,这两个文件夹下的,都是“common”的
接下来把一些特殊的单个资源也加入到shaderDependObjs里面
- 整合所有要打包的资源的List并且进行打包
遍历singlePrefabs
{
AssetBundleBuild currentBuildInfo = new AssetBundleBuild();
每一个prefab对应一个后缀名变为".assetbundle"的AssetBundleBuild信息
并且把这个AssetBundleBuild加入到singleBundleBuildList中
}
然后遍历shaderDependObjs,把里面的东西组成一个AssetBundleBuild commonBuildInfo
然后把上singleBundleBuildList和commonBuildInfo都加入到totalBundleBuildList中(1111个)
最后调用BuildPipeline.BuildAssetBundles(loadPath, totalBundleBuildList.ToArray(),
BuildAssetBundleOptions.ChunkBasedCompression, buildTarget);
通过model的例子所以可以看出来,所有的shader是一个包,每一个单独的模型的prefab是一个包
我们再看一个UI的例子
首先获取路径下Prefab,路径分别是UI/Prefab下面的三个子文件夹,Common | Login | Main
Common里面其实放的是Login和Main通用的基本模块
通过EditorUtility.CollectDependencies获取依赖项Object[] XXXUIdependencies
再把XXXUIdependencies中使用的所有Texture, Shader, AudioClip等资源路径加入commonDependPathList
loginUIdependencies中使用的这些资源的路径还要单独加到loginCommonUIDependPathList
然后从
commonDependPathList中除掉loginUIdependencies
因为loginUIdependencies中是只有login使用的,那么我们进入真正的游戏场景之后,就可以卸载掉,所以分开
接下来收集XXXUIdependencies所依赖的font资源路径以及texture资源路径加入buildList
接下来把各种DependPathList加入buildList
接下来把这三个文件夹的prefab加入buildList
最后BuildPipeline.BuildAssetBundles进行打包
总结一下这个过程就是Common | Login | Main文件夹下面的prefab找到dependency
然后把dependency的资源先加入buildList,然后是三个文件夹本身的prefab加入buildList
Login的特殊处理
通过model和UI的例子可以理解的是,怎么控制打多少个bundle,怎么组合,其实就是看怎么组装这个AssetBundleBuild[] buildMap
电脑上和手机上打出来的Assetbundle不能混用,不同平台只能用自己的。
Step 2:
BundleEditor.GenerateInfoData();
主要就是更新hash值,是用来做“差异更新”的
具体做的就是从文件里面读出hash,md5等信息,加入到
Dictionary<string, UpdateHelper.FileInfo> dicGenerateFiles里面
然后通过UpdateAssetBundleHash进行更新
然后在streamingAssets/VersionData/update.info文件,用jsonWriter写入dicGenerateFiles
一个manifest文件例子:
CRC: 3624793840
Hashes:
AssetFileHash:
serializedVersion: 2
Hash: 3c9f7f934d8b35aacec174736b363ebc
TypeTreeHash:
serializedVersion: 2
Hash: e6f1ab53fd1773d13eb3a725343cda21
对于table表来说,因为不打assetbundle,所以其hash值和md5是一样的
Step 3:
ReadResVersionInfoData
获得appverstion和commitversion
从/ VersionData/ResVersionInfo.txt读取
Step 4:
从streamingAssets/VersionData/ResVersion.txt
读取出versionMaintainer.InternalResVersion 并且+1 写回
这个读取的位置是 版本机上的 unity自己的一个数据结构,叫做
PlayerPrefsVersionMaintainer中的BuildCountKey
Windows是写在注册表里的?
Step 5:
在/VersionData/ResVersionInfo.txt读取配置是否要打热更新包
如果要打,首先确定输出文件夹:MDJClient\Release\ResData\
打热更新包的过程:
1 ReadAppInternalCurrentBuildResVersionDesc()到StreamingAssets\VersionData\ ResVersion.txt中取读取appVersion和resVersion (拆开来读)
2 CompareHistoryUpdateInfo (target)比较历史版本
首先ReadAllHistoryFileInfo读取所有的历史版本
(StreamingAssets/VersionData/ResVersion.txt中读取)
遍历UpdateInfoSaveFolder文件夹下所有的文件到allFiles
把这些文件作为Dictionary返回
作为历史版本信息
然后再从Assets\StreamingAssets\VersionData\ update.info读出一个Dictionary作为当前的版本信息
然后对于当前版本里面的每一个文件,查找历史版本里面看有没有
如果有的话,则对于场景文件对比hash,而其他文件对比md5,以确定是否需要更新
新的文件当然也需要更新
返回所有需要更新的文件
然后
GenerateNeedUpdateFileInfo
再根据Assets\StreamingAssets\VersionData\ update.info读出所有需要更新的文件的
生成一个Release\UpdateInfoSaveFolder/update.info. versionString这样一个路径名
然后Utils.GenerateResFileList(path, dic);
创建path目录
用jsonWriter写文件,名字就是path?
https://blog.****.net/u012141979/article/details/50412552
然后
GenerateUpdateFiles
对于所有的needUpdateFiles中的文件
从streamingAssets下面拷贝到/Release/ResData/{0}/StreamingAssets下面
然后
GenerateResVersionFile
streamingAssets/VersionData/ResVersion.txt拷贝到Release/UpdateInfoSaveFolder/下面
Step 6:
把streamingAssets/VersionData/ update.info
拷贝到Release\UpdateInfoSaveFolder\
文件名为currentVersionString + ".info"
Step 7:
BuildTableBundle_Internal
首先把所有streamingAssets /Tables下的*.txt|*.bytes打成一个名为tableZipFile.txt的zip包放到/DJMain/Resources下面,这个是专门为android平台准备的,因为android平台下streamingassets目录是个压缩目录没有办法直接用file.read,所以先拷贝到/DJMain/Resources下面然后再解压缩到可读写目录里面进行读写
把StreamingAssets/ Tables拷贝到Assets/BundleAssets/TempTables/下面
获得所有.bytes文件
通过BuildPipeline.BuildAssetBundles(outputPath + BundleManager.PathTableData, assetBundleBuildList.ToArray(), BuildAssetBundleOptions.ChunkBasedCompression | BuildAssetBundleOptions.DeterministicAssetBundle, target);
把这些文件打包成tablefilelist.assetbundle到StreamingAssets/Tables/
然后把Assets/BundleAssets/TempTables/删掉
StreamingAssets\Tables\tablefilelist.assetbundle这个主要是为了android在读可读写目录读不到的情况,比如还没有加压,需要用unity api去读这个tablefilelist.assetbundle文件,但是问题是如果热更新了,那么这里就不是最新的或者不存在的,所以热更新表解决不了zip包解压之前就需要读取的表的更新问题
所以获取table表存储目录:1 可读写目录 2 streamingassets目录 3 resource目录
resource目录里面存储的是table表的zip包,主要是用来游戏启动的时候 解压zip包到可读写目录(CopyTableToPersistentDataPath)为了android读表
路径总结:
虽然路径有点多,但是总得来说就是从streamingAssets下面先自己做各种打包操作,然后写到或者拷贝到Release/下面
版本信息拷贝到Release/UpdateInfoSaveFolder/下面
包内容拷贝到Release/ResData/下面
Table相关的特殊处理
总得来说就是这样的思路
====
BuildAssetBundles这个函数的解释:
https://docs.unity3d.com/ScriptReference/BuildPipeline.BuildAssetBundles.html
其中的第二个函数原型:
public static AssetBundleManifest BuildAssetBundles(string outputPath,
AssetBundleBuild[] builds, BuildAssetBundleOptions assetBundleOptions,
BuildTarget targetPlatform);
其中BuildAssetBundleOptions.ChunkBasedCompression 表示Use chunk-based LZ4 compression when creating the AssetBundle
这个算法比较快但是压缩率不高
a.Resources(只读)
Resources文件夹下的资源会被全部打包到apk或者ipa里面,相当与一个unity默认的AssetBundle,按理说是可以将游戏中所有的资源全部放在Resources进行加载,但是
Resources下的文件数量会影响游戏的启动速度,越多越慢。其次游戏为了降低drawcall,需要对相同材质的图像打包图集,但是如果放在Resources里面是没法打包图集的。
Resources下的资源打包时会进行压缩
b.StreamingAssets(只读)
StreamingAssets文件夹下的资源会被全部打包到apk或者ipa里面,其下的资源不会影响游戏启动速度,不会被压缩,所以占用内存较大
c.persistentDataPath(可读写)
persistentDataPath文件夹在unity项目中是不存在的,他是程序的沙盒文件夹,只有你游戏安装完成之后,才会存在这个目录,但是它可读写。
Resources里面放的资源只是在本地开发阶段使用,打包的时候会把Resources中的文件都考本出去打成AssetBundle包,然后再将打包好的AssetBundle放到StreamingAssets文件夹下打包apk。
游戏运行时再将资源考本到persistentDataPath文件夹下,因为这个目录可读写,所以能够用来做资源热更新