定义和作用
- AssetBundle(简称AB包)是一个资源压缩包,包含模型、贴图、预制体、声音、甚至整个场景,可以在游戏运行的时候被加载;
- AssetBundle自身保存着互相的依赖关系;
- 压缩包可以使用LZMA和LZ4压缩算法,减少包大小,更快的进行网络传输;
- 把一些可以下载内容放在AssetBundle里面,可以减少安装包的大小;
什么是AssetBundle
- 它是一个存在于硬盘上的文件。可以称之为压缩包。这个压缩包可以认为是一个文件夹,里面包含了多个文件。这些文件可以分为两类:serialized file 和 resource files。(序列化文件和源文件)
- serialized file:资源被打碎放在一个对象中,最后统一被写进一个单独的文件(只有一个)
- resource files:某些二进制资源(图片、声音)被单独保存,方便快速加载,可以Editor上读取,方便查看
- 它是一个AssetBundle对象,我们可以通过代码从一个特定的压缩包加载出来的对象。这个对象包含了所有我们当初添加到这个压缩包里面的内容,我们可以通过这个对象加载出来使用。
AssetBundle使用流程
- 1.指定资源的AssetBundle属性
- 2.构建AssetBundle包
- 3.上传AB包
- 4.加载AB包和包里面的资源
1.指定资源的AssetBundle属性
在Inspector面板最下面设置,必须要选择Project资源。名称不分大小写,后缀随意
如下图
设置如图可以设置后缀
设置文件
在设置的时候用/表示路径
如图
效果如下
2.构建AssetBundle包
需要使用编辑器扩展功能
创建脚本
脚本需要引用using UnityEditor,脚本需要放在Editor文件下
using System.IO;
using UnityEditor;
public class CreateAssetBundles{
[MenuItem("Assets/AssetBundles")] //创建菜单
static void CreateAssetBulesMain()
{
string dir = "AssetBundle"; //构建AssetBundle文件夹
if(Directory.Exists(dir) == false) //判断是否存在该文件
{
//不存在,则创建
Directory.CreateDirectory(dir);
}
//dir:构建AssetBundle的输出路径,BuildAssetBundleOptions资源包构建选项(比如说压缩算法,None是LZMA压缩,可查看官网), BuildTarget:构建的平台
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
}
}
选择构建
在Unity编辑器上可看到,在代码中写的创建菜单,选择构建
路径默认是根目录,和项目同级别
AssetBundle 文件
AssetBundle 文件是一个存档,在内部包含多个文件,其中包含在运行时为了加载资源而需要加载的内容。
结构如下
manifest文件
可用记事本打开,里面包含诸如循环冗余校验 (CRC) 数据和捆绑包的依赖性数据之类的信息。
ManifestFileVersion: 0
CRC: 345363325 //校验码 检测文件的完整性
Hashes:
AssetFileHash:
serializedVersion: 2
Hash: 92dcfeedcf2b24cd9b9fccc9834f11b8
TypeTreeHash:
serializedVersion: 2
Hash: c15c595f80747130a8d34dfc8ec48e63
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: 65
Script: {instanceID: 0}
Assets: //该包所包含的资源
- Assets/Prefabs/Cube.prefab
Dependencies:
- E:/UnityProject/AssetBundleProject/AssetBundle/decal //资源依赖关系,就比如Object打包出来,里面由材质等信息
加载manifest文件
//获取AssetBundle所依赖的包
string path2 = "http://localhost:8080/AssetBundle/AssetBundle";
UnityWebRequest request2 = UnityWebRequest.GetAssetBundle(path2);
yield return request2.Send();
AssetBundle ab2 = (request2.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
//名称必须时"AssetBundleManifest"
AssetBundleManifest manifest = ab2.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
foreach (string name in manifest.GetAllAssetBundles())
{
//获取AssetBundle加载清单
Debug.Log(name);
}
//获取指定文件所依赖的包
string path3 = "http://localhost:8080/AssetBundle/";
string[] strs = manifest.GetAllDependencies("cube.unity3d");
foreach (string name in strs)
{
//根据依赖包名加载指定的文件
UnityWebRequest request3 = UnityWebRequest.GetAssetBundle(path3 + name);
yield return request3.Send();
AssetBundle ab3 = (request3.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
Debug.Log(name);
}
3.上传AB包
可查看最后服务器
4.本地加载AssetBundle包
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoadFromFile : MonoBehaviour {
void Start () {
//加载AssetBundle包
AssetBundle ab = AssetBundle.LoadFromFile("AssetBundle/cube1/cube.unity3d");
//取得资源 如果是多个资源同一个AssetBundle名称,返回的是数组
GameObject cube = ab.LoadAsset<GameObject>("Cube");
//实例化
Instantiate(cube);
}
}
运行前删除Cube资源,运行后可看到Cube资源被加载到场景上
AssetBundle分组策略
逻辑实体分组
- 一个UI界面或者所有UI界面一个包(这个界面里面的贴图和布局信息一个包)
- 一个角色或者所有角色一个包(这个角色里面的模型和动画一个包)
- 所有的场景所共享的部分一个包(包括贴图和模型)
按照类型分组
所有声音资源打成一个包,所有shader打成一个包,所有模型打成一个包,所有材质打成一个包
按照使用分组
把在某一时间内使用的所有资源打成一个包。可以按照关卡分,一个关卡所需要的所有资源包括角色、贴图、声音等打成一个包。也可以按照场景分,一个场景所需要的资源一个包
总结
- 把经常更新的资源放在一个单独的包里面,跟不经常更新的包分离
- 把需要同时加载的资源放在一个包里面
- 可以把其他包共享的资源放在一个单独的包里面
- 把一些需要同时加载的小资源打包成一个包
- 如果对于一个同一个资源有两个版本,可以考虑通过后缀来区分 v1 v2 v3 unity3dv1 unity3dv2
AssetBundle依赖
依赖打包
如果一个或多个 UnityEngine.Objects 包含对位于另一个捆绑包中的 UnityEngine.Object 的引用,则 AssetBundle 可以变为依赖于其他 AssetBundle。如果 UnityEngine.Object 包含对任何 AssetBundle 中未包含的 UnityEngine.Object 的引用,则不会发生依赖关系。在这种情况下,在构建 AssetBundle 时,捆绑包所依赖的对象的副本将复制到捆绑包中。如果多个捆绑包中的多个对象包含对未分配给捆绑包的同一对象
的引用,则每个对该对象具有依赖关系的捆绑包将创建其自己的对象副本并将其打包到构建的 AssetBundle 中。
举例
假如:两个Object都引用相同的材质,两个对象分别打包,而材质不选择打包。则Unity在两个Object的AssetBundle打包时候检查与其它资源是否有依赖关系,有的话就检查是否也AssetBundle,否则分别打包进两个Object里,也就是把材质分别打包进两个Object里,而这就导致两个Oject的资源体积增大。而解决这种情况可以采用上面总结的第三点,把材质也单独打包出来。可以查看下面例子
不按依赖关系打包
只对Object打包
效果如下
按依赖关系打包
把两个Object共享的资源也单独打包出来
依赖加载
如果 AssetBundle 中包含依赖项,则在加载尝试实例化的对象之前,务必加载包含这些依赖项的捆绑包。Unity 不会尝试自动加载依赖项
示例
以上面依赖打包为例,由三个文件:2个Object的AssetBundle包,一个材质AssetBundle包,要使用Object的话,要先加载使用资源的AssetBundle包。查看下面示例
不按依赖关系加载
只加载需要的Object
void Start () {
//加载AssetBundle包
AssetBundle ab = AssetBundle.LoadFromFile("AssetBundle/cube.unity3d");
//取得资源 如果是多个资源同一个AssetBundle名称,返回的是数组
GameObject cube = ab.LoadAsset<GameObject>("Cube");
//实例化
Instantiate(cube);
}
效果如下
可以发现Object材质丢失
按依赖关系加载
使用Object前,先加载所需资源
void Start () {
//加载AssetBundle包
AssetBundle material = AssetBundle.LoadFromFile("AssetBundle/decal");
AssetBundle ab = AssetBundle.LoadFromFile("AssetBundle/cube.unity3d");
//取得资源 如果是多个资源同一个AssetBundle名称,返回的是数组
GameObject cube = ab.LoadAsset<GameObject>("Cube");
//实例化
Instantiate(cube);
}
效果如下
可以看到材质加载出来了
AssetBundle加载方式
- AssetBundle.LoadFromMemoryAsync
- AssetBundle.LoadFromFile
- WWW.LoadfromCacheOrDownload
- UnityWebRequestAssetBundle
LoadFromMemoryAsync
从内存中加载,也相当于在本地加载,以字节流和异步的方式,或者同步方式AssetBundle.LoadFromMemory
示例
IEnumerator Start () {
//加载AssetBundle包
string path = "AssetBundle/cube.unity3d";
AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return request;
AssetBundle ab = request.assetBundle;
//取得资源 如果是多个资源同一个AssetBundle名称,返回的是数组
GameObject cube = ab.LoadAsset<GameObject>("Cube");
//实例化
Instantiate(cube);
}
LoadFromFile
从本地加载,在上面已经介绍了,也有异步方式AssetBundle.LoadFromFileAsync
LoadfromCacheOrDownload
可从本地也可从服务器上加载,第一次加载资源会缓存在本地,下次加载会从本地进行加载。该方法已启用,推荐使用UnityWebRequestAssetBundle
UnityWebRequestAssetBundle
功能和LoadfromCacheOrDownload一样,可以加载本地,也可以加载服务器上的
IEnumerator Start () {
//加载AssetBundle包
string path = "http://localhost:8080/AssetBundle/cube.unity3d";
UnityWebRequest request = UnityWebRequest.GetAssetBundle(path);
yield return request.Send();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
//也可以使用这种
//AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
//取得资源 如果是多个资源同一个AssetBundle名称,返回的是数组
GameObject cube = ab.LoadAsset<GameObject>("Cube");
//实例化
Instantiate(cube);
}
AssetBundle的卸载
- 减少内存的使用
- 有可能导致丢失
在什么时候卸载
在切换场景,或者确定不使用的时候卸载
卸载方法
AssetBundle.Unload(true)
卸载所有资源,即使有资源被使用着。关切切换、场景切换和资源没被用的时候调用
AssetBundle.Unload(false)
卸载所有没用被使用的资源
Resources.UnloadUnusedAssets
卸载个别资源,场景切换的时候会自动卸载资源,就不需要使用该方法
AssetBundles浏览工具
查看和编辑资源包的配置
使用
1.下载Editor文件
下载上面链接的项目或者Release里下载
2.导入
将Editor文件导入到项目中,必须是指定的Editor文件
打开界面
进入Window->AssetBundle Browser
如图所示
搭建简单的服务器
采用Nodejs搭建
步骤
- 初始化nodejs环境:npm init
- 创建server.js 输入下面的代码
- 创建默认文件夹
- 启动server node server
var http = require('http');
var fs = require('fs');//引入文件读取模块
var documentRoot = 'E:/NodejsServer/act'; //默认文件路径
//需要访问的文件的存放目录
var server= http.createServer(function(req,res){
var url = req.url;
//客户端输入的url,例如如果输入localhost:8888/index.html
//那么这里的url == /index.html
var file = documentRoot + url;
console.log(url);
fs.readFile( file , function(err,data){
/*
err为文件路径
data为回调函数
回调函数的一参为读取错误返回的信息,返回空就没有错误
data为读取成功返回的文本内容
*/
if(err){
res.writeHeader(404,{
'content-type' : 'text/html;charset="utf-8"'
});
res.write('<h1>404错误</h1><p>你要找的页面不存在</p>');
res.end();
}else{
res.writeHeader(200,{
'content-type' : 'text/html;charset="utf-8"'
});
res.write(data);//将index.html显示在客户端
res.end();
}
});
}).listen(8080);