AssetBundle打包步骤超级详解
(一)ab是什么,打包流程简述
ab是一种资源的加载方式,是一种压缩文件的格式
为什么需要ab?
如果一个游戏的所有资源都打包成一整个资源,那游戏包会很大,如果是每个关卡的资源,每个关卡的加载,包会小点,所以ab就是把各种类型的资源如音乐,图片,场景,音效等变成一个ab包放到服务器上,然后客户端联网后,从服务器上下载,在本地动态加载资源,以达到资源更新的目的
ps:更新有资源热更和代码热更,代码热更需要通过c#+xlua框架的方式
(二)打包步骤
1.设置打包对象
2.生成ab包
3.上传到服务器
4.加载ab包中的资源
5.使用ab包中的资源
(三)步骤一:设置打包对象
这里的assetbundle是打包的包名,后面的那个none是扩展名
即可以给这个方块资源设置成包名为fangkuai,扩展名为u3d
(四)用代码生成ab包
1.assets下新建一个Editor文件夹,因为打包是在编辑下进行的。
2.在这个Editor文件夹下建立一个脚本,脚本在编辑状态下,所以不继承monobehaviour,继承Editor,且引入命名空间using UnityEditor;
注意:不要运行unity点击按钮打包,直接等编译完,点击按钮就可以了。
using UnityEditor;
using System.IO;//创建文件夹要用的包
public class MyTools
{
[MenuItem("Tools/CreateAssetBundles")]//在uinty上建一个菜单Tools下有个CreateBundles的菜单
static void CreateAssetBundles()//这是点击这个按钮的功能
{
//打包的路径
string assetBundleDirectory = "StreamingAssets";
//如果没有就在工程下创建一个这样的路径
if (!Directory.Exists(assetBundleDirectory))
{
Directory.CreateDirectory(assetBundleDirectory);
}
//打包资源:参数解析:assetBundleDirectory:打包到哪里,目录
//BuildAssetBundleOptions:none默认的打包方式
//BuildTarget:打包的平台
BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneOSX);
UnityEngine.Debug.Log("打包完成");
}
}
编译完打包出来的目录是这样的
点开streamingAssets是这样的资源清单和一些依赖关系
如何打包归类:
1.每种类型打个包
2.或者一个界面,关卡的资源打个包。
打包要注意的:
如果不把模型用的mat.u3d打包,而是把这三个预置体打包,那么这三个预置体中的材质都会被打包一次,每个预制体大概是20多kb,如果是把mat.u3d打包了,每个预制体的材质也相当于打包了,每个预制体是几kb。
(五)读取assetbundle的四种方式(内存,文件,www,unitywebrequest)
建立一个脚本,将脚本绑定到空对象上
注意:加载都用协程
(1)从内存中加载ab包中的单个资源
public class LoadAssetBundle : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
StartCoroutine(LoadFromMemory());
}
//从内存读取ab包
IEnumerator LoadFromMemory()
{
//要找的ab包里的资源的路径
string boxPath = @"Desktop/LittleSomethingsTest/StreamingAssets/box.u3d";
string matPath = @"Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
//把资源读取成byte[]
byte[] boxBytes = File.ReadAllBytes(boxPath);
byte[] matBytes= File.ReadAllBytes(matPath);
//加载ab包
AssetBundle boxAB = AssetBundle.LoadFromMemory(boxBytes);
AssetBundle matAB = AssetBundle.LoadFromMemory(matBytes);
//取出ab包里的资源,名字不是打包的名字,是从捆绑包中加载名为 name 的资源,原始的名字
GameObject boxPrefab = boxAB.LoadAsset<GameObject>("Cube");
//实例化资源
Instantiate(boxPrefab);
yield return null;
}
}
注意:这里因为事材质也分别打了包,所以加载ab包时,不能光加载模型,而是要把模型的材质也加载进来,不然模型丢失材质会变粉。
(2)从文件中加载ab包中的单个资源
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class LoadAssetBundle : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
StartCoroutine(LoadFromFile());
}
//从单个文件中读取ab包
IEnumerator LoadFromFile()
{
//要找的ab包里的资源的路径,材质也要加载
string boxPath = @"Desktop/LittleSomethingsTest/StreamingAssets/box.u3d";
string matPath = @"Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
//加载资源包和材质包
AssetBundle boxAB = AssetBundle.LoadFromFile(boxPath);
AssetBundle matAB = AssetBundle.LoadFromFile(matPath);
//取出ab包的资源实例化
GameObject boxPrefab = boxAB.LoadAsset<GameObject>("cube");
Instantiate(boxPrefab);
yield return null;
}
}
(3)将多个资源打入一个ab包,然后从文件读取ab包中的多个资源
1.把要打包的模型设置成同一个包名allobjects,扩展名都是u3d,材质单独一个包名mat
如图所示
2.代码实现
//从文件中读取ab包中的多个资源对象
IEnumerator AllFromFile()
{
//要加载的ab包路径
string prefabsPath = @"Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
string matPath = @"Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
//从文件加载ab包
AssetBundle prefabsAB = AssetBundle.LoadFromFile(prefabsPath);
AssetBundle matAB = AssetBundle.LoadFromFile(matPath);
//从ab包中加载所有资源
Object[] objList = prefabsAB.LoadAllAssets();
//遍历这些资源,然后实例化
foreach(var go in objList)
{
Debug.Log(((GameObject)go).name);
Instantiate(((GameObject)go));
}
yield return null;
}
(4)www方式无缓存机制的加载ab包的方式
www有两种方式,本地和http的,下面是本地方式
//用www的方式无缓存机制本地加载ab包中的资源
IEnumerator FromWWW1()
{
//资源的www
string prefabsPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
WWW prefabsWWW = new WWW(prefabsPath);
yield return prefabsWWW;//等www加载完,似乎这句不加也不影响加载?
if (!string.IsNullOrEmpty(prefabsWWW.error))//如果加载有错就输出错误
{
Debug.Log("加载错误" + prefabsWWW.error);
}
//使用加载出来的ab包
AssetBundle prefabsAB = prefabsWWW.assetBundle;
Object[] objList = prefabsAB.LoadAllAssets();
foreach(var go in objList)
{
Instantiate(((GameObject)go));
}
//材质的www
string matPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
WWW matWWW = new WWW(matPath);
//yield return matWWW;//注意如果这里加了这句,加载场景的时候prefab会先变粉再变成有材质的颜色,这是为什么呢……
AssetBundle matAB = matWWW.assetBundle;//这里别忘了材质的ab包
yield return null;
}
(5)带有缓存机制的www
//有缓存机制的www方式加载ab包:即当缓存有时用缓存里的ab包,缓存没有时,再去地址下载ab包
IEnumerator FromWWWHC()
{
string prefabsPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
//参数解析:第一个是下载ab包的路径,第二个是哈希数值用来识别版本号,当要求加载的路径和版本号都对上时,从缓存中加载,即版本号用来识别是缓存里的ab包的哪一个下载了的版本。
WWW prefabsWWW = WWW.LoadFromCacheOrDownload(prefabsPath, 1);
if (!string.IsNullOrEmpty(prefabsWWW.error))//如果加载有错就输出错误
{
Debug.Log("加载错误" + prefabsWWW.error);
yield break;
}
AssetBundle prefabsAB = prefabsWWW.assetBundle;
Object[] objList = prefabsAB.LoadAllAssets();
foreach (var go in objList)
{
Instantiate(((GameObject)go));
}
//材质的ab包
string matPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
WWW matWWW = WWW.LoadFromCacheOrDownload(matPath, 1);
AssetBundle matAB = matWWW.assetBundle;
yield return null;
}
ps:www对象还可以释放,通过添加
prefabsWWW.Dispose();
prefabsWWW = null;
(6)用unityWebRequest方式(新版本)
引入命名空间:using UnityEngine.Networking;
//用uintyWebRequest加载ab包
IEnumerator FromUnityWebRequest()
{
string prefabsPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
UnityWebRequest prefabsRequest;
prefabsRequest = UnityWebRequestAssetBundle.GetAssetBundle(prefabsPath);//命令从这个路径获取ab包
yield return prefabsRequest.SendWebRequest();//命令之后,还要发送这个命令
//api注释调用此方法后,UnityWebRequest 将执行 DNS 解析(如有必要),将一个 HTTP 请求传输到位于目标 URL 的远程服务器,并处理该服务器的响应。
//在任何给定的 UnityWebRequest 对象上,此方法只能调用一次。
AssetBundle prefabsAB = DownloadHandlerAssetBundle.GetContent(prefabsRequest);//从unitywebrequest中的对象获取内容
Object[] objList = prefabsAB.LoadAllAssets();
foreach (var go in objList)
{
Instantiate(((GameObject)go));
}
//读取资源
string matPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
UnityWebRequest matRequest;
matRequest = UnityWebRequestAssetBundle.GetAssetBundle(matPath);
yield return matRequest.SendWebRequest();//如果把这个注释掉,这个matrequest就是个未完成的请求对象,后面材质获取不到
AssetBundle matAB = DownloadHandlerAssetBundle.GetContent(matRequest);
yield return null;
}
ps:这种写法会预制体先试粉的,然后有材质,所以,可以让材质在预制体之前加载就可以了。写的顺序换一下。
(7)从文件通过主manifest文件加载所有的ab包
//以文件方式为例,通过主manifest文件加载所有资源和所有依赖项
IEnumerator LoadAssetsFromManifest()
{
string manifestPath = @"Desktop/LittleSomethingsTest/StreamingAssets/StreamingAssets";
AssetBundle manifestAB = AssetBundle.LoadFromFile(manifestPath);
//注意这里manifest的名字不是文件夹中的StreamingAssets名字,而是固定的AssetBundleManifest
AssetBundleManifest manifest = manifestAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// manifest.GetAllAssetBundles()这个获得的是个string[],是所有ab包的名字及其扩展名的一个字符串数组
foreach(string str in manifest.GetAllAssetBundles())
{
string abPath = @"Desktop/LittleSomethingsTest/StreamingAssets/" + str;
AssetBundle ab = AssetBundle.LoadFromFile(abPath);//每一个ab包中可能有多个资源,遍历然后实例化
object[] objList = ab.LoadAllAssets();
foreach(var go in objList)//因为这里是所有的ab包,其中也包含了不是游戏物体的材质,所以这里要判断一下,是材质才创建实例
{
//因为这里是所有的ab包,其中也包含了不是游戏物体的材质,所以这里要判断一下,是材质才创建实例
//不然转换会报错
if (go is GameObject)
{
Instantiate(((GameObject)go));
}
}
}
yield return null;
}
(8)从文件,通过主manifest加载特定ab包的依赖项
//以文件方式为例,通过主manifest文件获取特定ab包的依赖
IEnumerator LoadDependenciesFromManifest()
{
string prefabsPath = @"Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
AssetBundle prefabsAB = AssetBundle.LoadFromFile(prefabsPath);
Object[] objList = prefabsAB.LoadAllAssets();
foreach(var go in objList)
{
Debug.Log(((GameObject)go).name);
Instantiate(((GameObject)go));
}
//上面这几行代码是加载了ab包中的预制体,但是因为没有加载其依赖项,所以都是粉色的
//下面通过主manifest添加资源的依赖项
string manifestPath = @"Desktop/LittleSomethingsTest/StreamingAssets/StreamingAssets";
AssetBundle manifestAB = AssetBundle.LoadFromFile(manifestPath);
AssetBundleManifest manifest = manifestAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
string[] strs = manifest.GetAllDependencies("allobjects.u3d");//得到这个包的所有依赖项的名字的字符串数组
//加载依赖项的ab包
foreach(string s in strs)
{
AssetBundle.LoadFromFile(@"/Users/Magician/Desktop/LittleSomethingsTest/StreamingAssets/"+s);
}
yield return null;
}
(9)服务器上加载ab包同理,改路径为地址就行
本文地址:https://blog.csdn.net/qq_42385019/article/details/107320383