BuildAssetBundle 读一读 看看热闹

目录

打包是干什么的?

Step 1:

Step 2:

Step 3:

Step 4:

Step 5:

Step 6:

Step 7:

路径总结:

BuildAssetBundles这个函数的解释:

打包是干什么的

Unity3D开发的游戏中,无论模型,音频,还是图片等,我们都做成Prefab,然后打包成Assetbundle,方便我们后面的使用,来达到资源的更新。

 

 

====

In Our Program:

版本机上打版本的时候会build资源,同步build热更新包

 

在MLDJCommand.cs下加入了一个菜单项

BuildAssetBundle 读一读 看看热闹

 

BuildAssetBundle一共分为7个Step:

 

Step 1

按照资源类型,顺序打包,这里其实是按照资源分类分别封装函数,具体要打成多少个assetbundle,是在每一个_Internal函数内部进行控制。

一个最基本的原则:想通的资源尽可能的打包到一起,他们共用一套资源。不相同的模型尽量分开打包。

总的来说是按照文件夹来build的。

 

BuildAssetBundle 读一读 看看热闹

 

举例:

BuildModelAssetBundle("/DJMain/BundleData/Model", "BundleData/Model",

"Assets/StreamingAssets" + BundleManager.PathModelPrefab, target, modelFileEnd);

参数是路径信息,最后modelFileEnd是后缀名

比如BuildModel的时候就是loadPath = "Assets/StreamingAssets/Model"

其实就是我们要build的资源所在的Unity的原路径

 

然后通过BuildDependencyAssetBundle打带有依赖的AssetBundle包

  1. 检查路径有效性

CheckTargetPath函数中回对这个目标路径做一些格式的转换,确保路径的存在工作

 

  1. 遍历所有prefab计算依赖项

通过调用BundleMaker.cs中的GetAssetInPath函数,遍历整个文件夹,获得两个字典

一个是singlePrefabs<路径,文件名>字典 (1110个)

一个是singleObjsDic<文件名,Object>字典

第二个字典是通过Unity API AssetDatabase.LoadMainAssetAtPath获取的assets文件夹下的所有特定类型的物体

然后把这些Object放入curLoadPrefab数组,用来计算依赖项

然后通过Unity Api EditorUtility.CollectDependencies 来计算curLoadPrefab里面的所有依赖资源 dependObjs

这一步的时间还是比较长的

 

  1. 计算 共享资源包

在这些dependObjs中,对于所有的UnityEngine.Shader类型的资源,如果是在"Assets/DJMain"下面,则加入shaderDependObjs里面,用来打一个共享包

然后把CommonEffectTexture和CommonTexture所用到的texture加到shaderDependObjs里面,通过名字能看出来,这两个文件夹下的,都是“common”的

接下来把一些特殊的单个资源也加入到shaderDependObjs里面

 

  1. 整合所有要打包的资源的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里面其实放的是LoginMain通用的基本模块

通过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的特殊处理

 

通过modelUI的例子可以理解的是,怎么控制打多少个bundle,怎么组合,其实就是看怎么组装这个AssetBundleBuild[] buildMap

 

电脑上和手机上打出来的Assetbundle不能混用,不同平台只能用自己的。

 

Step 2:

BundleEditor.GenerateInfoData();

主要就是更新hash值,是用来做“差异更新”的

具体做的就是从文件里面读出hashmd5等信息,加入到

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是一样的

BuildAssetBundle 读一读 看看热闹

 

 

Step 3

ReadResVersionInfoData

获得appverstioncommitversion

/ 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中取读取appVersionresVersion (拆开来读)

 

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.txtzip包放到/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.assetbundleStreamingAssets/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文件夹下,因为这个目录可读写,所以能够用来做资源热更新