一、创建ab资源
(一)Unity资源设置ab格式
1、选中要打包成assetbundle的资源;
可以是图片,材质球,预制体等,这里方便展示用预制体打包设置展示;
2、AssetBundle面板说明
(1)初始面板
点击资源的Inspector面板最下面AssetBundle选项按钮,会弹出AssetBundle面板;
AssetBundle选项面板如下,默认两个位置都是None;设置输入后,按回车键确认;
在左边名称设置里有按钮选项,可一键清除不用的、弃用的名称和后缀。
输入的英文字母,即使是大写也会自动转为小写;所以不用考虑大小写问题;
(2)设置面板
从左往右
第一个设置,表示资源打包后在AB文件夹里的资源名,可设置多层级目录;
第二个设置,表示资源打包后的后缀名,同资源名都可以自定义设置无格式要求。
1) fish image 表示在AB文件夹里生成一个fish.image资源;
2) video/play/dead music 表示在AB文件下里生成一个video文件夹,在video文件夹里生成一个play文件夹,在play文件夹里生成一个dead.music资源;
3、关于资源名称,ab包名称和加载路径说明
上面和下面两张图结合着看;
(1)资源信息与所在位置
unity里名为Fish的预制体,生成的ab资源名为prefab.imaged,
文件所在的地址是:.../装有ab资源的文件夹/ab/prefab.image;
(2)获取资源及所需信息
加载ab资源的地址是:path = .../装有ab资源的文件夹/ab/prefab.image;
即AssetBundle ab = AssetBundle.LoadPath(path);
获取ab包里的某一资源是:resourceName = Fish (注意,不是prefab.image!!!)
即GameObject obj = ab.LoadAsset(resourceName);
(二)编辑器工具栏打包
1、Editor文件夹
编辑器功能的脚本要放到专门的Editor文件夹下,普通脚本正常放在Scripts文件夹下;
初始项目中不存在,同Resource文件夹,自己新建一个即可。
关于Unity一些特殊文件夹,可以看我其他博文,有博文介绍讲解;
2、自定义打包设置
打包生成ab资源的脚本CreateABpack里,打包功能按钮自定义放在File文件下,如下图;
打包功能的按钮名自定义为Build AssetBundles;可根据需求自己选择设置放置;
3、功能使用
非项目运行状态下,点击下图的Build AssetBundles按钮,即可打包项目中所有设置了ab设置的资源;
(三)C#生成ab包的脚本
1、相关库
使脚本在编辑器环境下可直接,需使用UnityEditor库;
在指定文件夹下存放生成的ab资源,需要使用System.IO库;
cs
using UnityEditor; //把该脚本当工具使用,使脚本在编辑器模式下就可以运行
using System.IO; //文件使用相关的库
2、自定义按钮名称和位置
使用特性:[MenuItem("显示在工具栏哪个文件夹下/该打包方法在菜单上显示的名称")]
用静态方法,使可用全局访问,实现非运行状态下也可以调用使用;
cs
[MenuItem("File/Build AssetBundles")] //方法做到File下,按钮名为Build AssetBundles
static void BuildAllAssetBundles(){} //静态方法,全局可以访问
3、打包资源
自定义打包后的输出路径:
cs
//相对路径,打包到工程里与Asset同级的AB_File文件夹下,不是Asset文件夹里的AB_File文件夹下
string path = "AB_File";
//绝对路径,打包到指定文件夹,C盘下面的AB_File文件夹下
string file = @"C:\AB_File";
可能存在输出的文件夹不存在,先判断文件夹存不存在,不存在则在目标位置生成目标文件夹。
cs
if (Directory.Exists(filePath) == false) //判断目标文件夹/相对路径是否存在
Directory.CreateDirectory(file); //相对路径不存在/目标文件夹不存在,则创建
使用API:BuildPipeline.BuildAssetBundles(输出文件地址,ab资源压缩格式,输出平台)
设置打包格式,具体参数自行查看api;
cs
//资源打包,参数:打包到的指定文件夹的路径,压缩方式,输出平台
BuildPipeline.BuildAssetBundles(filePath, BuildAssetBundleOptions.None,
BuildTarget.StandaloneWindows64);
4、功能代码
cs
using UnityEditor;
using System.IO;
public class CreateABpack
{
[MenuItem("File/Build AssetBundles")]
static void BuildABundles()
{
//string filePath = "AssetBundlesFile";
string filePath = @"C:\AB_File";
if (Directory.Exists(filePath) == false)
Directory.CreateDirectory(filePath);
BuildPipeline.BuildAssetBundles(filePath, BuildAssetBundleOptions.None,
BuildTarget.StandaloneWindows64);
}
}
二、3种ab包加载方式
(一)本地直接加载:LoadFromFile
1、方法说明
(1)AssetBundle ab = AssetBundle.LoadFromFile(filePath);
这是加载 AssetBundle 的最快方法。
同步加载中,将等待 AssetBundle 对象创建完毕才返回。
在主线程中执行,若资源过大,加载创建时间过长,则建议使用异步加载。
FilePath是文件在磁盘上的路径,可支持相对路径(相对项目,文件在项目中的路径位置)。
(2)T asset= ab.LoadAsset<T>(resName);
从ab包里加载资源,资源目标格式为T;如Texture,GameObject等;
resName是资源在Unity中的资源名. 如:Fish
cs
//从文件中加载AssetBundle,文件路径,一直到ab资源名后缀结束
//如:C:\AB_File\ab\prefab.image
AssetBundle.LoadFromFile(FilePath);
//从ab包里加载资源,资源目标格式为T;
//resName是资源在Unity中的资源名. 如:Fish
T asset= ab.LoadAsset<T>(resName);
2、对应的异步加载API
AssetBundle.LoadFromFileAsync();
3、其他同步加载方式
AssetBundle.LoadFromMemory;AssetBundle.LoadFromStream;
4、功能代码
cs
using System.Collections.Generic;
using UnityEngine;
public class LoadABpack : MonoBehaviour
{
private string localFile = @"C:\AB_File\";
public static Dictionary<string, GameObject> prefabDic = new Dictionary<string, GameObject>();
public void ClickBut()
{
LoadResource(localFile + @"ab\prefab.image", "Fish");
}
/// <summary>
/// 直接加载本地ab包
/// </summary>
/// <param name="resName"></param>
/// <param name="filePath"></param>
public void LoadResource(string filePath, string resName)
{
if (!prefabDic.ContainsKey(resName))
{
AssetBundle ab = AssetBundle.LoadFromFile(filePath);
GameObject obj = ab.LoadAsset<GameObject>(resName);
prefabDic.Add(resName, obj);
ab.Unload(false);
}
UsingResource(resName);
}
public void UsingResource(string resName)
{
if (prefabDic.ContainsKey(resName)) Instantiate(prefabDic[resName]);
else Debug.Log("No AssetBundles");
}
}
(二)本地异步加载:LoadFromFileAsync
1、方法说明
(1)AssetBundleCreateRequest abRequest = AssetBundle.LoadFromFileAsync(filePath);
声明的是AssetBundleCreateRequest类型,不是AssetBundle类型!!!
注意与同步加载的方法进行对比比较;
(2)使用异步协程方法实现
一般使用协程执行;需要使用到using System.Collections;库
对于协程,不能使用静态方法调用,即调用协程的方法不能是static修饰的;
cs
//这种是错误的
public static void ClickBut()
{
StartCoroutine(Asyn_LoadAB(localFile, resName));
}
//这样才正确使用
public void ClickBut()
{
StartCoroutine(Asyn_LoadAB(localFile, resName));
}
//协程实现异步加载ab资源
IEnumerator Asyn_LoadAB(string filePath, string resName)
{
//注意声明的类型!!!
AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(filePath);
yield return request;
AssetBundle ab = request.assetBundle;
T res = ab.LoadAsset<T>(resName);
}
2、对应的同步加载API
AssetBundle.LoadFromFile(FilePath);
3、其他异步加载方式
AssetBundle.LoadFromMemoryAsync;AssetBundle.LoadFromStreamAsync;
4、功能代码
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoadABpack : MonoBehaviour
{
private string localFile = @"C:\AB_File\";
public static Dictionary<string, GameObject> prefabDic = new Dictionary<string, GameObject>();
public void ClickBut()
{
StartCoroutine(Asyn_LoadAB(localFile + @"ab\prefab.image", "Fish"));
}
/// <summary>
/// 异步加载本地AB包
/// </summary>
/// <param name="resName"></param>
/// <param name="filePath"></param>
IEnumerator Asyn_LoadAB(string filePath, string resName)
{
if (!prefabDic.ContainsKey(resName))
{
AssetBundleCreateRequest abRequest = AssetBundle.LoadFromFileAsync(filePath);
yield return abRequest;
AssetBundle ab = abRequest.assetBundle;
GameObject obj = ab.LoadAsset<GameObject>(resName);//加载资源
yield return obj != null;
prefabDic.Add(resName, obj);//存储资源
ab.Unload(false);
}
UsingResource(resName);
}
public void UsingResource(string resName)
{
if (prefabDic.ContainsKey(resName)) Instantiate(prefabDic[resName]);
else Debug.Log("No AssetBundles");
}
}
(三)服务器异步加载:UnityWebRequest
1、方法说明
注意!!!
(1)正确使用
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(filePath);
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
(2)错误使用1
UnityWebRequest request = UnityWebRequest.Get(url);
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request); //报转化格式无效
(3)错误使用2
UnityWebRequest request = UnityWebRequest.Get(url);
AssetBundle ab =(request.DownloadHandler as DownloadHandler).assetbundle; //结果是ab= null
2、特别说明:
(1)静态类使用UnityWebRequest
静态类UnityWebRequest.Get创建的Request,自带DownloadHandler和UploadHandler;
cs
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(filePath);
yield return request.SendWebRequest();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
或
UnityWebRequest request = UnityWebRequest.Get(url);
yield return request.SendWebRequest();
string strData = request.downloadHandler.text;
(2)构造函数使用UnityWebRequest
构造函数new UnityWebRequest 创建的Request,没有的,需要自己手动创建赋值!!!
cs
UnityWebRequest request =new UnityWebRequest(uri);
DownloadHandlerFile download = new DownloadHandlerFile();
request.downloadHandler = download;
yield return request.SendWebRequest();
3、UnityWebRequest其他方法
除了获取服务器数据的Get方法,还支持Post,Put,Abort,Head方法
**Get:**一般用于向服务器获取信息;但Get访问时有信息暴露的风险;
**Post:**获取服务器信息,但Post访问时内容不会暴露,安全性更高;
**Put:**将数据发送到远程的服务器。比如文件上传;
**Abort:**会尽快结束联网,可以随时调用此方法。
方法调用后,如果 UnityWebRequest未完成,那么将会尽快停止上传或下载数据;
head: 只请求页面的首部;与Get方法几乎是一样;
Head使用时会检查超链接的有效性;检查网页是否被修改;
该方法多用于自动搜索机器人获取网页的标志信息,获取rss种子信息,或者传递安全认证信息等
3、其他服务器加载方式
WWW方式: WWW request= new WWW(url); 但现在Unity已经弃用。
4、功能代码
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class LoadABpack : MonoBehaviour
{
private string localhost = @"http://localhost/AB_File/";
public static Dictionary<string, GameObject> prefabDic = new Dictionary<string, GameObject>();
public void ClickBut()
{
StartCoroutine(LoadSeverResource(localhost + @"ab\prefab.image", "Fish"));
}
/// <summary>
/// 异步获取服务器上ab资源
/// </summary>
/// <param name="resName"></param>
/// <param name="filePath"></param>
/// <returns></returns>
IEnumerator LoadSeverResource(string filePath, string resName)
{
if (!prefabDic.ContainsKey(resName))
{
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(filePath);
yield return request.SendWebRequest();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
GameObject obj = ab.LoadAsset<GameObject>(resName);
yield return obj;
prefabDic.Add(resName, obj);
ab.Unload(false);
}
UsingResource(resName);
}
public void UsingResource(string resName)
{
if (prefabDic.ContainsKey(resName)) Instantiate(prefabDic[resName]);
else Debug.Log("No AssetBundles");
}
}
三、补充说明及相关报错解决
(一)UnityWebRequest获取不同资源数据使用方法会不同;
关于服务器获取数据,详见可查看本人另一篇服务器获取xlua文件数据的博文;
关于数据解析,以及Json格式数据处理及相关Json插件使用,也有对应的博文欢迎查看;
(二)重新再次加载ab包,出现报错
The AssetBundle can't be loaded because another AssetBundle with the same files is already loaded.
1、加载使用完ab包,没有及时卸载;(出现该报错,基本是这个原因)
ab包加载后要卸载,不卸载再次执行加载同一个ab包时,会出现上述报错;
如:我在主游戏场景中加载ab资源,然后跳转回开始场景,然后再运行到主游戏场景,
由于之前主场景运行加载的ab资源没有卸载,这次再运行主场景,就会报该错误。
解决方法:
在加载ab资源后卸载ab包,注意加载完再后卸载,不然容易出现找不到ab资源问题。
cs
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
GameObject obj = ab.LoadAsset<GameObject>(resName);
yield return obj;//等待资源获取完成,卸载ab
ab.Unload(false);
2、服务器获取ab包和解析ab包是同一帧。
有时候获取如UI资源,刚获取到资源,相应文件还处于打开中缓存在内存中,此时Unity检测到已获取资源,立即执行解析使用资源操作,但因内存中还存在打开的ab文件的文件,被认为相同文件已经被打开加载,故报上述错误。
解决方法:
在获取/下载外部资源的文件系统释放后,再调用解析资源的方法;可在调用解析方法前加上yield return new WaitForSeconds(0.1f);
cs
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
yield return new WaitForSeconds(0.1f);
GameObject obj = ab.LoadAsset<GameObject>(resName);
cs
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
yield return new WaitForSeconds(0.1f);
GameObject obj = ab.LoadAsset<GameObject>(resName);
3、ab包资源设置的名称相同
资源名相同,后缀不同的单个资源是允许的;但是名相同,后缀相同,即使路径不同也会报错;
解决方法:
生成ab资源时,注意资源名与资源后缀的设置,不要有重复命名;