1 给要打包的资源设置标记,表示对应的包名:
2 Unity5 AssetBundle不需要我们来管理引用关系了
3 可以使用代码批量设置包名
AssetImporter ai = AssetImporter.GetAtPath(assetPath);
i.assetBundleName = xxx;
ai.assetBundleVariant = xxx;
Create a folder called Editor in the Assets folders, and place a script with the following contents in the folder:
using UnityEditor;
public class CreateAssetBundles
{
[MenuItem("Assets/Build AssetBundles")]//菜单选项
static void BuildAllAssetBundles()
{
string assetBundleDirectory = "Assets/AssetBundles";//打包到哪里
if(!Directory.Exists(assetBundleDirectory))
{
Directory.CreateDirectory(assetBundleDirectory);
}
BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
}
}
BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
是主要的打包函数。
其中第二个参数,
BuildAssetBundleOptions
BuildAssetBundleOptions.None:使用LZMA算法压缩,压缩的包更小,但是加载时间更长。使用之前需要整体解压。一旦被解压,这个包会使用LZ4重新压缩。使用资源的时候不需要整体解压。在下载的时候可以使用LZMA算法,一旦它被下载了之后,它会使用LZ4算法保存到本地上。
BuildAssetBundleOptions.UncompressedAssetBundle:不压缩,包大,加载快
BuildAssetBundleOptions.ChunkBasedCompression:使用LZ4压缩,压缩率没有LZMA高,但是我们可以加载指定资源而不用解压全部。
注意使用LZ4压缩,可以获得可以跟不压缩想媲美的加载速度,而且比不压缩文件要小。
https://docs.unity3d.com/Manual/AssetBundles-Building.html
调用该函数,unity会自动根据资源的标签进行打包,而且是增量打包,
a.对于资源没有变更的bundle包,不会触发重新打包;
b.资源没变,即使生成目录下的bundle包被删除了,unity也不会重新打包;
c.生成目录下的bundle包对应的manifase被删了,会重新打包;
d.可以使用BuildAssetBundleOptions.ForceRebuildAssetBundle参数触发强制重新打包。
5 生成的bundle包资源目录:
StreamingAssets:一个AssetBundle包,内含AssetBundleManifest类型的Asset,记录了所有bundle包及相互间的依赖关系。
运行时需要首先加载这个AssetBundleManifest,然后根据其提供的depence信息加载依赖的bundle包。
StreamingAssets.manifest:全局manifest,全局manifest的名字和打包生成的目录同名,不是固定的,这里是生成在StreamingAssets目录下。
Cube.assetbundle:资源bundle。
Cube.assetbundle.manifest:每个资源自己的manifest,与bundle一一对应,只是用来做增量build,运行时根本不需要。
^_^场景打包成AssetBundle资源和普通资源打包有所区别。
这是一个普通的assetbundle
The Scene AssetBundle is different to normal AssetBundles, in that it is optimized for stream loading of a Scene and its content.
Scene AssetBundle略有不同,为
加载场景Scene AssetBundle用到的接口:
SceneManager.LoadScene()或者SceneManager.LoadSceneAsync()
第二个参数决定了是否销毁之前加载的GameObject
manifest文件里面定义了使用的资源和依赖:
加载Manifests文件可以处理资源的依赖
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest =
assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
string[]dependencies = manifest.GetAllDependencies("assetBundle"); //Pass the name of the bundleyou want the dependencies for.
foreach(string dependency in dependencies)
{
AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency));
6压缩格式
(1)LZMA:默认压缩格式,压缩比大,省空间,使用前需要解压整个压缩包;
(2)LZ4:5.3版本新增, 40%–60% 的压缩率,开启BuildAssetBundleOptions.ChunkBasedCompression打包参数即可。
LZ4算法是“基于块”的,因此当对象从一个LZ4压缩包加载时,仅用于该对象的相应块会被解压,不需要解压整个包。
所以LZ4和不压缩资源一样,都可以不解压,而直接读取包中的资源的。
7 AssetBundle加载方式对比
主要的加载方式有以下四种,前两种还兼具下载的功能,后两种都有对应的异步接口:
使用哪一种方法取决于bundle的提供形式:
(1)WWW.LoadFromCacheOrDownload:走本地cache,没有就下载并解压(然后再LZ4压缩),有就用;
如果没有缓存,对于未压缩的和LZ4压缩的AssetBundle包,unity会直接把它们拷贝到缓存目录里面,对于LZMA压缩的,会先解压然后重新压缩成LZ4格式,然后缓存它。可以通过Caching.compressionEnabled控制是否压缩缓存。
(2)LoadFromFile:最快的方式,区别于4.x版本,可以直接使用压缩资源;
如果是UnCompress或LZ4,直接从disk读取
如果是LZMA,会先解压到memory,然后读取
For users intending to load from local storage, you’ll be interested in the AssetBundles.LoadFromFile API. Which looks like this:
public class LoadFromFileExample extends MonoBehaviour {
function Start() {
var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle"));
if (myLoadedAssetBundle == null) {
Debug.Log("Failed to load AssetBundle!");
return;
}
var prefab = myLoadedAssetBundle.LoadAsset.("MyObject");
Instantiate(prefab);
}
}
(3)LoadFromMemoryAsync:从内存加载,一般用于加密资源。
//异步加载资源
AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path1));
yield return request;
//加载共同依赖资源,如贴图、材质
AssetBundleCreateRequest request2 = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path2));
yield return request2;
AssetBundle ab = request.assetBundle;
AssetBundle ab2 = request2.assetBundle;
//使用里面的资源
GameObject wallPrefab1 = ab.LoadAsset("CubeWall");
Instantiate(wallPrefab1);
(4)UnityWebRequest
首先是创建一个web request(调用UnityWebRequest.GetAssetBundle), 然后进行资源的获取(调用DownloadHandlerAssetBundle.GetContent)
StartCoroutine(InstantiateObject())
;
IEnumerator InstantiateObject() { string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityEngine.Networking.UnityWebRequest request =
UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0); yield return request.Send(); AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request); GameObject cube = bundle.LoadAsset
("Cube"); GameObject sprite = bundle.LoadAsset ("Sprite"); Instantiate(cube); Instantiate(sprite); }
使用方式建议:
(1)随包资源StreamingAssets:
未压缩或LZ4压缩:LoadFromFile;
LZMA压缩:可使用 WWW.LoadFromCacheOrDownload解压缩到本地磁盘。
(2)热更新资源:LZMA+WWW.LoadFromCacheOrDownload+Caching.compressionEnabled;
(3)加密资源:LZ4+LoadFromMemory;
(4)自己压缩的资源:UncompressAssetBundle的AssetBundle包+自己的算法压缩+LoadFromFileAsync。
WWW.LoadFromCacheOrDownload会被UnityWebRequest取代
8 资源卸载
AssetBundle.Unload(false):干掉压缩包,bundle不再可用,即不能再通过bundle.Load加载资源;
AssetBundle.Unload(true):干掉压缩包,和所有从中加载(load)出来的资源。
所以卸载资源一般有两种玩法:
(1)AssetBundle.Unload(false)结合Resource.UnloadUnusedAssets()
(2)碎片化使用AssetBundle.Unload(true)
具体怎么用要结合游戏本身的数据特点来定制。关于要不要Unload释放AssetBundle本身的内存,也有两种主流玩法,一种是即用即卸(LoadAsset以后立马释放AssetBundle),一种是缓存AssetBundle不卸载,两种方法各有优劣,需结合使用。
unity在场景中的Object被移除的时候不自动释放objects,资源的清理需要再特定的时间触发(场景切换)或者手动的管理。所以怎么加载和卸载资源显得尤为重要,不合适的加载可能会导致资源的重复加载,不合适的卸载可能会带来资源的缺失(比如丢失贴图)。
对于assetbundle的资源管理,最重要的是掌握什么时候调用AssetBundle.Unload(bool)这个函数,传入true/false会有不同的卸载策略。这个API会卸载对应的assetbundle的头部信息,参数对应着是否同时卸载从该assetbundle中实例化的所有Objects。
AssetBundle.Unload(true)会卸载assetbundle中的所有gameobjects以及其依赖关系,但是并不包括基于其Objects实例化(复制)的Object(因为这些object不属于该assetbundle,只是引用),所以当卸载贴图相关的assetbundle的时候,场景中对其引用的实例化物体上会出现贴图丢失,也就是场景中会出现红色的区域,unity都会将其处理成贴图丢失。
举例说明,假设材质M来自于assetbundle AB, 如果 AB.Unload(true), 那么场景中任何M的实例都会被卸载和销毁,如果AB.Unload(false), 那么就会切断材质M实例与AB之间的关系:
那么如果该assetbundle AB在后面再次被加载,unity不会重新关联其关系,这样在后续的使用中,就会出现一份材质的多个实例:
所以通常情况下,AssetBundle.Unload(false) 并不能带来较为合理的释放结果,AssetBundle.Unload(true)通常用来确保不会在内存中多次拷贝同一个资源,所以其更多的被项目所采纳,此外还有两个常用的方法用来确保其使用:
1)在游戏中,对于场景的卸载有明确的规划,比如在场景切换中或者场景加载中;
2)管理好对每个单独的object的计数,只有在没有引用的时候才卸载该assetbundle,这样可以规避加载和卸载过程中的多份内存拷贝问题。
如果要使用AssetBundle.Unload(false), 那么这些实例化的对象可以通过2中途径卸载:
1)清除对不需要物体的所有引用,场景和代码中都需要清楚,然后调用Resources.UnloadUnusedAssets;
2) 在场景加载的时候采用非增量的方式加载,这会清楚当前场景中的所有Objects,然后反射自动调用Resources.UnloadUnusedAssets
如果你不想管理这些assetbundle,unity推出了AssetBundle Manager,可以学习了解一下,此外Unity还推出了一些AssetBundle Browser Tool, 也可以学习了解一下。
9 校验
CRC MD5 SHA1
相同点:
CRC、MD5、SHA1都是通过对数据进行计算,来生成一个校验值,该校验值用来校验数据的完整性。
不同点:
1.算法不同。CRC采用多项式除法,MD5和SHA1使用的是替换、轮转等方法;
2.校验值的长度不同。CRC校验位的长度跟其多项式有关系,一般为16位或32位;MD5是16个字节(128位);SHA1是20个字节(160位);
3.校验值的称呼不同。CRC一般叫做CRC值;MD5和SHA1一般叫做哈希值(Hash)或散列值;
4.安全性不同。这里的安全性是指检错的能力,即数据的错误能通过校验位检测出来。CRC的安全性跟多项式有很大关系,相对于MD5和SHA1要弱很多;MD5的安全性很高,不过大概在04年的时候被山东大学的王小云破解了;SHA1的安全性最高。
5.效率不同,CRC的计算效率很高;MD5和SHA1比较慢。
6.用途不同。CRC一般用作通信数据的校验;MD5和SHA1用于安全(Security)领域,比如文件校验、数字签名等。
10
Unity Asset Bundle Browser tool