AssetBundle -- 用于资源的更新html
为了以后的xLua (Lua热更新的框架)打下基础git
AssetBundle的学习 -- 一手学习资源:UnityManual -> AssetBundles -> 教程式的文档github
AssetBundle是一个压缩包(也能够认为是一个文件夹)
包含模型Model、贴图Texture、预制体Prefab、声音AudioClip、甚至整个场景
在游戏运行的时候能够被加载算法
这个压缩包被打包出来存在硬盘中,里面包含的文件能够分为两类:serialized file和resource files
serialized file:资源,如模型、预制体,被打碎放在一个对象中,最后统一被写进一个单独的文件
resource file:某些二进制资源,如图片、声音,被单独保存,方便快速加载
-- 一个单独的图片或声音就会被打包成一个.resource文件数组
压缩包可使用LZMA和LZ4压缩算法,便于更快的进行网络传输 -- 区别见任务13浏览器
为何会用到网络传输呢?
1. 把一些能够下载的资源放在AssetBundle中,而不放在app中,而在须要的时候从网上直接加载资源
-- 减小安装包的大小
安装包过大:影响用户体验,或致使用户不想下载
2. 方便更新
-- 对于某些模型,若是进行某些活动更新,用户不须要更新安装包
进入游戏后,检查资源更新,从服务器上下载模型并加载便可
第一次启动后,或以后有更新时,下载更新包到本地,以后就不用下载了缓存
AssetBundle自身保存着互相的依赖关系
好比一个包是专门保存模型的,而另外一个包是专门保存贴图的,那这两个包之间就会有依赖关系安全
AssetBundle在加载时,做为一个AssetBundle对象,对象中包含的内容就是AssetBundle压缩包中的内容服务器
https://docs.unity3d.com/Manual/AssetBundles-Workflow.html网络
1. 指定资源的AssetBundle属性
2. 构建AssetBundle包
AssetBundle包能够有多个,每一个AssetBundle包中能够有多个文件
Unity会搜寻项目中的资源,若是资源被标记了须要打包,则会被打包到AssetBundle包中
3. 上传AssetBundle包
通常状况下都会上传到服务器
4. 加载AssetBundle包及其中的资源
启动游戏以后,游戏会在服务器上检查更新,将更新包下载到本地,加载上面的资源
1. 指定资源的AssetBundle属性 -- 详见任务10~12
从AssetStore上搜索Material资源
建立一个Cube,拉伸成一个墙壁的形状,将mat资源附加上
制做成一个名为wall的prefab
修改wall prefab的AssetBundle标签,New -> Wall (不区分大小写)
前面也能够写成 aaa/bbb的格式,表示在aaa文件夹下生成名为bbb的assetbundle文件
好比:scene/wall
后缀写成assetbundle,不是必要的,随意填写都行
设置成相同的assetbundle属性的不一样物品,会被打包到相同的assetbundle文件中
2. 使用Unity的API进行构建AssetBundle包 -- 详见任务13
这个API是只有在Editor模式下才运行的
建立Editor文件夹
新建CreateAssetBundles.cs脚本
using UnityEditor;
不继承自MonoBehaviour
BuildPipeline.BuildAssetBundles(string outputPath, BuildAssetBundleOptions option, BuildTarget platform);
string path; // path是以工程的根目录为基础的 -- 输出目录须要已存在,Unity不会自动建立路径
所以须要进行路径是否存在的判断
using System.IO;
if(!Directory.Exists(path)) {
Directory.CreateDirectory(path);
}
BuildAssetBundleOptions.None // 表示不去设置该选项 -- 选项解释详见任务13
BuildTarget.StandaloneWindows64 // 表示为了window64平台打包的 -- 不一样平台之间的ab包不能够交叉使用
using UnityEditor; using System.IO; public class CreateAssetBundle { [MenuItem("Assets/BuildAssetBundles")] static void BuildAllAssetBundles() { string path = "AssetBundles"; if (!Directory.Exists(path)){ Directory.CreateDirectory(path); } BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); }}
[MenuItem("Assets/BuildAssetBundles")] // 表示这个方法在菜单栏Assets下建立子菜单BuildAssetBundles
Unity中,Assets -> 点击BuildAssetBundles,就会进行AssetBundle的打包了
Unity中的Project下没有任何变化,
在系统中打开工程根目录,会发现新建了一个AssetBundles文件夹
wall.assetbundle就是wall的prefab文件
wall.assetbundle.manifest记录了wall.assetbundle的依赖项
AssetBundles是为当前目录
AssetBundles.manifest当前目录生成了哪一些assetbundle文件
.manifest是文本文件,一个assetbundle文件对应一个manifest文件
记录了对应的assetbundle的依赖,详见任务14
如何删除不用的assetbundle名字呢
好比如今发现咱们的assetbundle名写错了,从新生成一个assetbundle
可是设置assetbundle属性的菜单中,会发现原来的assetbundle名字没有被替换,依然存在
在设置资源的AssetBundle属性的地方,选择remove unused names
如今,在设置assetbundle属性的菜单里就找不到不用的assetbundle名了
3. 上传AssetBundle包
由于在开发阶段,咱们要重复对AssetBundle包进行修改,所以不会进行上传到服务器这一操做
开发阶段直接将AssetBundle包放在本地,再进行加载
4. 加载AssetBundle包 -- 详见任务16
这里先讲解加载本地的AssetBundle包
加载本地的AssetBundle包和加载服务器上的AssetBundle包所用的API是不一样的
如今,将咱们的场景下的物品和Project中的prefab清空
为了更清楚地看到物体是从AssetBundle中加载出来的
建立一个空物体,命名LoadAssetBundle
添加脚本LoadAssetBundleFromLocal.cs
在Start()中进行加载操做
AssetBundle.LoadFromFile("AssetBundles/scene/wall.assetbundle");
// 须要加后缀
返回值为AssetBundle类型的对象 -- 见任务4&5:
AssetBundle在加载时,做为一个AssetBundle对象,对象中包含的内容就是AssetBundle压缩包中的内容
AssetBundle assetBundle = ...;
GameObject wallPrefab = assetBundle.LoadAsset<GameObject>("Wall");
// 对象类型为GameObject,名为"Wall",将加载到的资源赋值给GameObject对象
// 这里的名字"Wall"不能乱写,须要和打包前的名字相同 -- Case-insensitive
Instantiate(wallPrefab);
另外一种方法是assetBundle.LoadAllAssets();
返回的是Object[],
能够经过foreach将全部Object[]中包含的prefabs都实例化出来
以后进行每一个阶段的详解
1. 资源的AssetBundle属性详解
AssetBundle的分组策略(仅供参考,需结合实际项目使用)
逻辑实体分组:
一个UI界面/ 全部UI界面 (这个界面里面的贴图和布局信息)一个包
一个角色/ 全部角色 (这个角色里面的模型和动画)一个包
全部的场景共享的部分 (全部共享的贴图和模型)一个包
类型分组:
全部声音资源 一个包
全部shader 一个包
全部模型 一个包
全部材质 一个包
全部动画 一个包 等等
按照使用分组:
在某一时间内使用的全部资源 一个包 -- 好比一个关卡中的全部资源
在一个场景中所需的全部资源 一个包
总结:
1. 把常常更新的资源放在一个单独的包中,跟不常常更新的包分离
-- 用户更新的时候能够减小下载量
2. 把须要同时加载的资源放在一个包中
-- 这些须要同时使用的资源,若是不在同一个包中,则须要加载多个包
3. 把其余包所共享的资源放在同一个包中
-- 好比:如有CubeWall和CapsuleWall共用了MaterialA,MaterialA中用了贴图TextureA
那么在打包CubeWall的时候,会将它所依赖的资源,如MaterialA和TextureA,和它打包到一块儿
对于CapsuleWall也是同样的,这样的话会致使MaterialA和TextureA被打包了屡次
资源打包的重复会使得assetbundle包变大
若是将MaterialA和TextureA打包到同一个包中,
这两个wall prefab去引用这个资源包中的material和texture,即依赖于这个资源包
4. 若是对于一个同一个资源有两个版本,能够考虑经过后缀来区分
-- 这两个版本都会使用到,好比角色升级前和升级后的模型
5. 把一些须要同时加载的小资源打包成一个包
-- 若是同时使用,且资源很小,那么就放在一个包中吧
由于加载包也是须要时间的,在同一个包中,就一会儿都加载出来了
注意是小资源
-- 任务12:依赖打包
以上述例子来解释
将共用的Material和Texture文件打入一个包,叫share
将CapsuleWall和CubeWall分别打入两个包,capsulewall和cubewall包
Unity会自动进行依赖控制
build asset bundle -->
查看assetbundle的大小为67.9kb
按原来的方式打包出来的大小将近两倍。
2. 构建AssetBundle包的详解
BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions, BuildTarget);
BuildAssetBundleOptions: 有不少选项
None:使用LZMA算法压缩 -- 高压缩比,可是加载的时间更长
使用以前须要总体解压,假如包中有三个资源,其中一个资源是要用的,可是必须所有解压才能使用
一旦被解压,这个包会使用LZ4从新压缩,这个时候使用资源时就不须要总体解压了。
LZ4算法 -- 使用资源时不须要总体解压,使用什么资源就解压哪个,加载速度快,可是压缩程度不大
即,在下载的时候使用LZMA算法--由于包小;以后会使用LZ4算法保存到本地--由于加载快
这个机制是Unity内置的优化策略
UncompressedAssetBundle:不压缩,包大,加载快
ChunkBasedCompression:使用LZ4算法压缩
压缩率没有LZMA高,但解压速度块,并且能够加载指定资源而不用总体解压
优势:能够得到跟不压缩相媲美的加载速度,并且又将包大小压缩了
实例对比:
使用None,AssetBundle大小为67.9KB
使用ChunkBasedCompression,AssetBundle大小为97.2KB
使用UncompressedAssetBundle,AssetBundle大小为151KB
以share.assetbundle.manifest为例
ManifestFileVersion: 0 CRC: 1445635609 -- CRC: 用于校验文件是否完整 Hashes: AssetFileHash: serializedVersion: 2 Hash: c009e7bf6581a024ac7d30c2196142f6 TypeTreeHash: serializedVersion: 2 Hash: cc983a3149e5fb03ce027e70c7a1a559 HashAppended: 0 ClassTypes: - Class: 21 Script: {instanceID: 0} - Class: 28 Script: {instanceID: 0} - Class: 48 Script: {instanceID: 0} Assets: -- 包中包含的文件 - Assets/Materials/Wood texture floor_01/Wood texture floor_01.png - Assets/Materials/Wood texture floor_01/Wood texture floor_01.mat Dependencies: [] -- share包没有依赖其余包,所以为空
以capsulewall.assetbundle.manifest为例
ManifestFileVersion: 0 CRC: 429711530 Hashes: AssetFileHash: serializedVersion: 2 Hash: e312559590875437e4b50f7f082675eb TypeTreeHash: serializedVersion: 2 Hash: 32a31884022770668140c4ee94dc0ded HashAppended: 0 ClassTypes: - Class: 1 Script: {instanceID: 0} - Class: 4 Script: {instanceID: 0} - Class: 21 Script: {instanceID: 0} - Class: 23 Script: {instanceID: 0} - Class: 33 Script: {instanceID: 0} - Class: 43 Script: {instanceID: 0} - Class: 136 Script: {instanceID: 0} Assets: - Assets/Prefabs/CapsuleWall.prefab Dependencies: -- 由于capsulewall依赖了share包中的材质和贴图,所以这里有了对share的依赖 - "D:/Programming/SikiUnityLearning/3-7AssetBundle/AssetBundleProject/AssetBundles/share.assetbundle"
对于资源的加载而言,若是一个资源有依赖,那么必须先去加载它的依赖,不然会出现资源 (好比贴图) 丢失的状况
就算当前Project中是存在那些材质资源的,也不行,须要加载匹配的依赖包中的资源才行
好比直接加载cubewall并实例化:
须要在加载cubewall以前加载share包 -- 不必定,详见下
AssetBundle sharedAssetBundle = AssetBundle.LoadFromFile("AssetBundles/share.assetbundle");
以后再加载cubewall .....
就能够获得完整的cubewall了
资源的依赖关系:
上面讲到假如MaterialA中使用到了TextureB,即MaterialA包依赖TextureB包;
被依赖的包TextureB必须先加载,才能进行加载MaterialA
其实否则:
正确的顺序为,包的加载并无严格的前后顺序
只要保证在使用到MaterialA资源以前,TextureB的包被加载完成,就行
以上述cubewall的例子为例
在使用cubewall资源以前
即在GameObject wallPrefab = assetBundle.LoadAsset<GameObject>("cubewall");以前
加载share包便可
可是在测试过程当中
同在Start()里,不管谁先谁后,均可以加载出带有mat的cubewall
很奇怪???和Documentation中说的不一样?
Consider the following example, a Material in Bundle 1 references a Texture in Bundle 2: In this example, before loading the Material from Bundle 1, you would need to load Bundle 2 into memory. It does not matter which order you load Bundle 1 and Bundle 2, the important takeaway is that Bundle 2 is loaded before loading the Material from Bundle 1.
作个测试:
在Start()中load cubewall,并instantiate一个cubewall
这时,会出现material丢失的状况
可是在Update()中经过Input.GetKeyDown()按下按钮后进行load share包
在按下按键以前,material仍是丢失的
可是按下按键以后,material就出如今被instantiate的cubewall上
嗯,很奇怪
这么作居然能够,可是保险起见,仍是按上面的前后顺序原则进行load吧
AssetBundle的加载分为四种:
AssetBundle.LoadFromMemoryAsync() -- 从内存中异步进行加载
AssetBundle.LoadFromFile() -- 从本地进行加载
WWW.LoadFromCacheOrDownLoad() -- 从缓存中(本地)或服务器上加载
UnityWebRequest.DownloadHandlerAssetBundle() -- 以前用WWW方法,但更新后推荐用这个方法从服务器加载
什么状况下用哪一个方法?
主要基于assetbundle是以什么形式提供的
AssetBundle.LoadFromMemoryAsync(byte[] binary, uint crc = 0);
将byte[]转换成AssetBundle对象
byte[] binary -- 如何获得byte[]呢?
能够经过http/tcp等协议获得
假如经过tcp协议下载获得了服务器端的assetbundle,这时获得的就是byte数组
直接使用LoadFromMemoryAsync()便可
或者也能够先保存到本地,再经过LoadFromFile()获得
也能够经过File.ReadAllBytes(path)获得
官方实例:
IEnumerator LoadFromMemoryAsync(string path) { AssetBundleCreateRequest createRequest=AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path)); yield return createRequest; AssetBundle bundle = createRequest.assetBundle; var prefab = bundle.LoadAsset<GameObject>("MyObject"); Instantiate(prefab); }
但这个实例的AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
就是实现了LoadFromFile(path)的从本地文件读取包的状况
AssetBundleCreateRequest 表示作了一个异步请求
须要等待Load...()加载完成
实例:
using UnityEngine; using System.IO; using System.Collections; public class LoadAssetBundleFromMemory : MonoBehaviour { IEnumerator Start () { // assetbundle的加载 string path = "AssetBundles/cubewall.assetbundle"; AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path)); // 异步方式的请求,须要等待Load完成 yield return request; AssetBundle assetBundle = request.assetBundle; // assetbundle的使用 GameObject wallPrefab = assetBundle.LoadAsset<GameObject>("cubewall"); Instantiate(wallPrefab); } }
LoadFromMemoryAsync()的变形
LoadFromMemory() -- 同步的方式
不须要进行yield暂停,由于同步的方式会等待Load的方法加载完才进行返回
使用方法和LoadFromFile类似 --
AssetBundle assetBundle = AssetBundle.LoadFromMemory(File.ReadAllBytes(path));
不须要进行yield return操做
void Start() { // assetbundle的加载 string path = "AssetBundles/cubewall.assetbundle"; AssetBundle assetBundle = AssetBundle.LoadFromMemory(File.ReadAllBytes(path)); // assetbundle的使用 GameObject wallPrefab = assetBundle.LoadAsset<GameObject>("cubewall"); Instantiate(wallPrefab); }
AssetBundle.LoadFromFile(string path)
从本地读取 -- 以前使用过
LoadFromFile()的变形
LoadFromFileAsync() -- 异步方式
Enumerator Start() { string path = "AssetBundles/cubewall.assetbundle"; AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path); // 等待Load加载完毕 yield return request; AssetBundle assetBundle = request.assetBundle; // 加载资源 GameObject wallPrefab = assetBundle.LoadAsset<GameObject>("cubewall"); Instantiate(wallPrefab); }
WWW.LoadFromCacheOrDownload()
将文件从服务器下载到本地的缓存目录中
This API is useful for downloading AssetBundles from a remote server or loading local AssetBundles.
Loading an AssetBundle from a remote location will automatically cache the AssetBundle.
If the AssetBundle is compressed, a worker thread will spin up to decompress the bundle and write it to the cache.
-- LZMA压缩算法时 (BuildAssetBundleOptions.None)
Once a bundle has been decompressed and cached, it will load exactly list AssetBundle.LoadFromFile()
官方实例:
public class LoadFromCacheOrDownloadExample : MonoBehaviour { IEnumerator Start () { while (!Caching.ready) // 等待Caching是否准备完成 yield return null; -- 表示暂停一帧 // Cache准备完成,则开始进行下载 // 5为版本号 // 若是是全新版本,会所有下载; // 若是版本相同,会进行检查,若是已经下载了,会从cache中load // 若是没有下载,则会从服务端进行下载 var www = WWW.LoadFromCacheOrDownload ("http://myserver.com/myassetBundle", 5); // 等待下载完成 yield return www; if(!string.IsNullOrEmpty(www.error)) { -- 记得进行错误检测 Debug.Log(www.error); yield return null; } var myLoadedAssetBundle = www.assetBundle; var asset = myLoadedAssetBundle.mainAsset; } }
实例:
public class LoadAssetBundleFromCacheOrDownload : MonoBehaviour { IEnumerator Start() { while (!Caching.ready) // 等待Caching是否准备完成 yield return null; // 本地文件的前缀 file:// 或 file:/// WWW www = WWW.LoadFromCacheOrDownload (@"file:///D:\...\AssetBundles\cubewall.assetbundle", 1); // 等待下载完成 yield return www; if (!string.IsNullOrEmpty(www.error)) { Debug.Log(www.error); yield return null; } AssetBundle assetBundle = www.assetBundle; // var asset = assetBundle.mainAsset; // Debug.Log(asset); GameObject wallPrefab = assetBundle.LoadAsset<GameObject>("cubewall"); Instantiate(wallPrefab); } }
如何从服务端下载,详见任务19
经过NetBox2.exe搭建简单Server服务器
在素材文件夹中找到NetBox2.exe
使用的是http协议
建立页面index.html做为首页,随意写一个内容
此时打开NetBox2.exe就会跳转到index.html页面
本地的Server端就开启了
将AssetBundles文件夹拷贝到NetBox2.exe所在目录
NetBox2.exe所在目录能够视做服务器端
那么如今就能够经过Unity到Server端下载AssetBundle了
经过WWW.LoadFromCacheOrDownload()从服务器端下载
经过http协议能够访问刚才搭建的服务器
访问到服务器就至关于访问到根目录,再经过url获得assetbundle的位置
WWW www = WWW.LoadFromCacheOrDownload(@"http://localhost/cubewall.assetbundle", 1);
测试:
第一次:打开Server,运行Unity project,获得实例化后的cubewall
第二次:关闭Server,运行Unity project,依然能获得实例化后的cubewall
可是从浏览器中访问不到localhost了
第三次:关闭Server,修改WWW.LoadFromCacheOrDownload()的版本号为2
此次就在www.error处输出了404 Not Found
总结:第二次能获得结果的缘由是,版本号相同的状况下,会进行检查,已经下载了,因此从cache目录load
第三次的版本号不一样,所以须要到服务器端下载,可是Server端关闭,所以404 Not Found
重点方法
取代了WWW.LoadFromCacheOrDownload(),并作了优化
官方实例:
IEnumerator InstantiateObject() { string uri ="file:///"+Application.dataPath+"/AssetBundles/"+assetBundleName; UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri, 0); yield return request.SendWebRequest(); AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request); GameObject cube = bundle.LoadAsset<GameObject>("Cube"); GameObject sprite = bundle.LoadAsset<GameObject>("Sprite"); Instantiate(cube); Instantiate(sprite); }
1. using UnityEngine.Networking;
2. UnityWebRequest.GetAssetBundle(uri,0);
// uri能够是本地路径也能够是server路径
3. unityWebRequest.SendWebRequest()
// 开始进行下载
4. DownloadHandlerAssetBundle.GetContent(request);
// 取得内容,返回一个AssetBundle的对象
(request.DownloadHandler as DownloadHandlerAssetBundle).assetBundle
request中的DownloadHandler强转为DownloadHandlerAssetBundle类型,自带assetBundle
实例:
using UnityEngine.Networking; public class LoadAssetBundleFromUnityWebRequest : MonoBehaviour { IEnumerator Start() { string uri = @"file:///D:\...\AssetBundles\cubewall.assetbundle"; UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri); // 这个时候还没从服务器端下载 // 调用了request.Send()以后,才会开始下载 yield return request.SendWebRequest(); // 获得AssetBundle -- 方法1 AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(request); // 获得AssetBundle -- 方法2 // 由于DownloadHandler有不少类型,转换为DownloadHandlerAssetBundle类型 AssetBundle assetBundle2 = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; // 实例化 GameObject wallPrefab = assetBundle.LoadAsset<GameObject>("cubewall"); Instantiate(wallPrefab); GameObject wallPrefab2 = assetBundle2.LoadAsset<GameObject>("cubewall"); Instantiate(wallPrefab2); } }
总结:
1. 经过UnityWebRequest.GetAssetBundle(uri)指定uri
2. 经过request.SendWebRequest() 进行assetbundle下载
3. 取得AssetBundle
两个方法
a. DownloadHandlerAssetBundle.GetContent(request);
b. request.DownloadHandler...assetBundle
将GetAssetBundle(uri)的路径改成服务器端的地址
UnityWebRequest.GetAssetBundle(@"http://localhost/AssetBundles/cubewall.assetbundle");
上面讲解了如何从本地/服务器获得AssetBundle,这节讲述如何从获得的AssetBundle里获得所需资源
四种加载资源的方式:
loadedAssetBundle.LoadAsset<T>(assetName);
loadedAssetBundle.LoadAssetAsync<T>(assetName);
loadedAssetBundle.LoadAllAssets();
T objectFromBundle = bundleObject.LoadAsset<T>(assetName);
好比GameObject object = assetBundle.LoadAsset<GameObject>("prefabName");
-- 和以前使用的方法相同
还有一种方式 -- LoadAllAssets()
Unity.Object[] objectArray = assetBundle.LoadAllAssets();
能够获得该包中的指定的全部资源
好比:存入了一个prefab,该prefab所使用的material会自动打包进来,可是unity自动打包的
这时,经过LoadAllAssets()就不会获得这个material资源
上述两种方法对应的异步方法:
当加载的资源较大时就可使用异步加载的方式
loadedAssetBundle.LoadAssetAsync<T>(assetName);
AssetBundleRequest request = loadedAssetBundle.LoadAssetAsync<GameObject>(assetName);
yield return request; // 等待加载完成
GameObject object = request.asset; // 取得加载到的资源
loadedAssetBundle.LoadAllAssetsAsync();
AssetBundleRequest request = loadedAssetBundle.LoadAllAssetsAsync();
yield return request; // 等待加载完成
Object[] objects = request.allAsset; // 取得加载到的资源
在任务15中,讲述了包之间的依赖关系
对于经过BuildPipeline.BuildAssetBundles(path, ...);
在path文件夹下存放那些assetbundle包
在这个path文件夹下,也会自动生成一个与文件夹名字相同的包
对于该包会有对应的一个manifest文件
接任务15,生成的AssetBundles.manifest的内容为
ManifestFileVersion: 0 CRC: 29988270 AssetBundleManifest: AssetBundleInfos: Info_0: Name: capsulewall.assetbundle Dependencies: Dependency_0: share.assetbundle Info_1: Name: cubewall.assetbundle Dependencies: Dependency_0: share.assetbundle Info_2: Name: share.assetbundle Dependencies: {}
包含了这个文件夹下全部的assetbundle包,和每一个包所依赖的包的信息
好比这个文件夹下有capsulewall.assetbundle, cubewall.assetbundle, share.assetbundle
依赖关系是capsulewall包依赖share包,cubewall包依赖share包,share包没有依赖其余包
想要知道包之间的依赖关系,只须要在程序中获得AssetBundles文件便可 (不是AssetBundles.manifest)
获得AssetBundleManifest的方式和获得其余资源的方式同样,有不少种
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath); // 这里即AssetBundles的路径
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
获得的manifest对象,与AssetBundles.manifest文件内容是对应的,包含了全部资源的列表和依赖关系
// 如何获得一个包的依赖关系呢
string[] dependencies = manifest.GetAllDependencies("assetName"); // 好比"cubewall"
// 经过这些依赖着的包名,加载这些包便可
foreach(string dependency in dependencies) {
AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency));
}
实例,以任务15为例
public class LoadAssetBundleWithDependencies : MonoBehaviour { private void Start() { string path = "AssetBundles/"; string assetBundleName = "CubeWall.assetbundle"; // 获得AssetBundles文件 AssetBundle manifestAssetBundle = AssetBundle.LoadFromFile(Path.Combine(path, "AssetBundles")); // 获得AssetBundles.manifest内容 AssetBundleManifest manifest = manifestAssetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); // 经过manifest中存储的包名,获得某个assetbundle的依赖 string[] dependencies = manifest.GetAllDependencies(assetBundleName); // 遍历依赖,并加载全部依赖 foreach(string dependency in dependencies) { AssetBundle.LoadFromFile(Path.Combine(path, dependency)); } // 加载assetbundle本dle AssetBundle assetBundle = AssetBundle.LoadFromFile(Path.Combine(path, assetBundleName)); GameObject wallPrefab = assetBundle.LoadAsset<GameObject>("cubewall"); Instantiate(wallPrefab); }}
卸载的优缺点:
减小内存使用
若是卸载不当,会致使丢失,好比卸载掉了某个正在运行的物体所依赖的资源时
Unity does not automatically unload Objects when they are removed from the active scene.
Asset cleanup is triggered at specific times, and it can also be triggered manually.
致使的问题: Improperly unloading an AssetBundle can bead to duplicating objects in memory or
other undesirable circumstances, such as missing textures;
assetBundle.Unload(bool)
-- unloads the header information of the assetBundle.
the bool arguments indicates whether to also unload all Objects instantiated from this assetBundle
Unload(true): unload the objects loaded from the assetBundle, even if they're being currently used in the active scene
-- this is what can cause textures or other things to go missing.
官方实例:assetbundle.Unload(true) 和assetbundle.Unload(false)的区别
MaterialM 在assetbundle中被加载
assetbundle.Unload(true):
Any instance of M in the active scene will also be unload and destroyed.
assetbundle.Unload(false):
break the chain of the current instances of M and assetbundle
if assetbundle is reloaded later and M is loaded by assetbundle.LoadAsset()
unity will not re-link the existing copies of M to the newly loaded Material.
-- there will be two copies of M loaded.
此时原来的M依旧被其余物体使用
若是它再也不被使用了,就会致使M依然占用内存,并且经过Unload()也没法卸载(由于Unload是卸载ab包的)
所以:using AssetBundle.Unload(false)
does not lead to an ideal situation.
Most projects should use AssetBundle.Unload(true)
to keep from duplicating objects in memory.
Most projects should use AssetBundle.Unload(true)
and adopt a method to ensure that Objects are not duplicated.
使用Unload(true)的两种思路:
1. Having well-defined points during the applications's lifetime at which transient Assetbundles are unloaded
好比不一样关卡的切换时,或正在加载新场景时调用unload(true)
2. Maintaining reference-counts for individual Objects, and unload only when all of their constituent Object are unused.
监测资源的使用,当全部资源不被使用时能够调用unload(true)
使用Unload(false)后,unload未卸载的资源(如MaterialM)的两种思路:
1. Eliminate all references to an unwanted Object, both in the scene and in code. -- 好比把引用设置为null
After the is done, call Resources.UnloadUnusedAssets()
2. Load a scene non-additively. This will destroy all Objects in the current scene -- 使用非附加的方式切换场景时
and invoke Resources.UnloadUnusedAssets() automatically.
从服务器传输过程当中,可能会出现字节传输丢失或出错
因此须要在使用文件以前进行文件是否完整传输的校验
文件校验:
经过文件的内容进行生成校验值,校验值的生成经过算法
CRC MD5 SHA1
在服务器端生成校验值后,进行文件的传输(with生成的校验值);
客户端下载到文件后,使用相同算法进行校验值的生成,将两个校验值进行对比便可
上述三种校验算法的区别:
1. 算法不一样:CRC为多项式除法;MD5和SHA1为替换、轮转等方法
2. 校验值的长度不一样:CRC校验位的长度和其多项式有关系,通常为16或32位;MD5为128位;SHA1为160位
3. 校验值的称呼不一样:CRC通常叫作CRC值;MD5和SHA1通常叫作哈希值或散列值
4. 检测能力不一样:CRC的安全性跟多项式有很大关系,比MD5和SHA1差不少;
MD5安全性很高;SHA1安全性最高
5. 效率不一样:CRC效率最高,和检测能力为逆相关
6. 用途不一样:CRC通常用做通讯数据的校验;MD5和SHA1通常用于安全领域,好比文件校验、数字签名等
Patching with AssetBundle -- 打补丁 -- 更新游戏的一些资源
须要注意的点:
How to detect which assetbundles to replace.
A patching system requires two lists of information:
1. A list of the currently downloaded AssetBundles, and their versioning information
2. A list of the AssetBundles on the server, and their versioning information
依赖包重复的问题:
三种解决方式:
1. 将须要共享的资源打包到一块儿,即若两个资源依赖于同一个资源,则将这两个资源打包到一块儿
缺点:
这些资源不必定同时使用;
资源更新时若是有一个资源是常常更新的,则整个包都须要更新
2. 分割包,这些包不在同一时间使用
缺点:可是这种方法会致使assetbundle的大小变大
3. 将被依赖的资源(须要被共享的资源)打包到一个包中 -- 推荐方法(具体问题具体分析)
图集重复问题:Sprite Atlas Duplication
Texture Type为Sprite 2D and UI下有一个属性,Packing tag
Unity会自动将这些2D图片打包到图集中
若是Packing tag不指定,则会打包到同一个图集中
这样的结果是:
若是将贴图A打包到A包中,由于贴图A在图集中,则会将整个图集打包到A包中
若是将贴图B打包到B包中,由于贴图B也在图集中,那么整个图集也会打包到B包中
-- 图集重复问题
解决方法:
将属于同一个图集的图片打包到同一个包中
将图片分属于不一样图集,经过修改图片的Packing Tag
Unity AssetBundle Broswer tool -- 浏览工具
用于清楚地显示BundleList、AssetList和对应的Details信息
https://github.com/Unity-Technologies/AssetBundles-Browser
在Release中下载,将Editor目录拖入Unity Project面板
如今,在Window->AssetBundleBroswer就能打开浏览面板了
能够显示各个assetbundle,和assetbundle中的资源信息,之间的dependency等等
在Build选项卡中
实现了BuildPipeline的那些parameter选项
Clear Folders -- 将以前生成的assetbundle文件删除,就不须要手动清空该文件夹了
StreamingAssets表示流资源,在build成安装包的时候,会把这个文件夹中的资源原封不动地放入安装包中
其余文件夹下的资源可能会自动进行压缩等操做
CopyToStreamingAssets选项将assetbundle拷贝到StreamingAssets文件夹下
为何呢,这样作会加大安装包大小
若是assetbundle不放在StreamingAssets下,那么第一次加载的时候仍是须要去下载这部分资源的
虽然安装包小,可是仍是须要进行下载的。
那么,索性就将游戏一开始运行就必须使用的资源放入。
结果就是,玩家安装完游戏就不须要再进行下载更新包了,除非有更新文件
点击Build,就能够生成了