首页 > 代码库 > 如何使用Assetsbundle打包,下载,加载
如何使用Assetsbundle打包,下载,加载
Directory 类 在system.io空间下,负责目录的管理和创建。Exists判断是否有该目录。
原文地址:http://blog.csdn.net/cuiyh1993/article/details/52245337
打包:打包的功能一定要放在Editor文件夹下,不然编译过程会出错。Editor文件夹下的脚本不能进行挂载。
using UnityEngine;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System;
using UnityEditor;
/// <summary>
/// 创建AssetBundle
/// </summary>
public class CreateAssetBundle : MonoBehaviour
{
//生成到的目录
public static string Build2Path = Application.dataPath + "/BuildABs";
public static BuildTarget target = BuildTarget.iOS;
[MenuItem("CYH_Tools/AB_Packager/Build_2_IPhone")]
public static void BuildiPhoneResource()
{
target = BuildTarget.iOS;
BuildAssetResource(target);
}
[MenuItem("CYH_Tools/AB_Packager/Build_2_Android")]
public static void BuildAndroidResource()
{
target = BuildTarget.Android;
BuildAssetResource(target);
}
[MenuItem("CYH_Tools/AB_Packager/Build_2_Windows")]
public static void BuildWindowsResource()
{
target = BuildTarget.StandaloneWindows;
BuildAssetResource(target);
}
private static void BuildAssetResource(BuildTarget target)
{
//文件已经存在就删除
if (Directory.Exists(Build2Path))
{
Directory.Delete(Build2Path, true);
}
//文件不存在就创建
if (!Directory.Exists(Build2Path))
{
Directory.CreateDirectory(Build2Path);
}
//打包
BuildPipeline.BuildAssetBundles(Build2Path,BuildAssetBundleOptions.None,target);
}
}
下载到本地,与资源的加载。这里的mono可以不继承,这样单例就可以简单一些,而且不需要挂载,但是底下的开启协程就要写的长一点(还要用mono对象去调用StartCoroutine)。
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System;
public delegate void dlg_OnAssetBundleDownLoadOver();
/// <summary>
/// 加载AssetBundle
/// </summary>
public class LoadAssetBundle :MonoBehaviour
{
public static LoadAssetBundle Instance ;
void Awake() {
Instance = this;
}
//不同平台下StreamingAssets的路径设置
// public static readonly string PathURL =
//#if UNITY_ANDROID
// "jar:file://" + Application.dataPath + "!/assets/";
//#elif UNITY_IPHONE
// Application.dataPath + "/Raw/";
//#elif UNITY_STANDALONE_WIN || UNITY_EDITOR
// "file://" + Application.dataPath + "/StreamingAssets/";
//#else
// string.Empty;
//#endif
//5.0版本打包时候选中需要打包的东西然后设置右下角名称,同个/设置多集目录,后面的框标记后缀(后缀不重要)
//打包时候的目标文件夹,假设目标文件夹名称为"WJJ",那么会生成"WJJ"和"WJJ.manifest"两个文件
//其中WJJ.manifest文件没有用,只是用来看的,WJJ是一个assetbundle包,里面包含了整个文件夹的依赖信息
//可以先加载这个东西,然后获取到依赖关系后逐步加载
//递一般归加载并保存到Application.persistentDataPath
//注意用GetDirectDependencies递归,不要用GetAllDependencies,因为已经包含孙子儿子又会加载孙子,重复加载了
//简单用法直接获取不要用GetAllDependencies,然后倒序加载
/// <summary>
/// 下载资源到本地包括它的依赖项
/// </summary>
/// <param name="AssetsHost">根目录地址</param>
/// <param name="RootAssetsName"></param>
/// <param name="AssetName"></param>
/// <param name="savePath"></param>
public void DownLoadAssets2LocalWithDependencies(string AssetsHost, string RootAssetsName, string AssetName, string savePath, dlg_OnAssetBundleDownLoadOver OnDownloadOver = null)
{
StartCoroutine(DownLoadAssetsWithDependencies2Local(AssetsHost, RootAssetsName, AssetName, savePath, OnDownloadOver));
}
/// <summary>
/// //从服务器下载到本地
/// </summary>
/// <param name="AssetsHost">服务器路径</param>
/// <param name="RootAssetsName">总依赖文件目录路径</param>
/// <param name="AssetName">请求资源名称</param>
/// <param name="saveLocalPath">保存到本地路径,一般存在Application.persistentDataPath</param>
/// <returns></returns>
IEnumerator DownLoadAssetsWithDependencies2Local(string AssetsHost, string RootAssetsName, string AssetName, string saveLocalPath, dlg_OnAssetBundleDownLoadOver OnDownloadOver = null)
{
WWW ServerManifestWWW = null; //用于存储依赖关系的 AssetBundle
AssetBundle LocalManifestAssetBundle = null; //用于存储依赖关系的 AssetBundle
AssetBundleManifest assetBundleManifestServer = null; //服务器 总的依赖关系
AssetBundleManifest assetBundleManifestLocal = null; //本地 总的依赖关系
if (RootAssetsName != "") //总依赖项为空的时候去加载总依赖项
{
ServerManifestWWW = new WWW(AssetsHost + "/" + RootAssetsName);
Debug.Log("___当前请求总依赖文件~\n");
yield return ServerManifestWWW;
if (ServerManifestWWW.isDone)
{
//加载总的配置文件
assetBundleManifestServer = ServerManifestWWW.assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
Debug.Log("___当前请求总依赖文件~\n");
}
else
{
throw new Exception("总依赖文件下载失败~~~\n");
}
}
//获取需要加载物体的所有依赖项
string[] AllDependencies = new string[0];
if (assetBundleManifestServer != null)
{
//根据名称获取依赖项
AllDependencies = assetBundleManifestServer.GetAllDependencies(AssetName);
}
//下载队列 并获取每个资源的Hash值
Dictionary<string, Hash128> dicDownloadInfos = new Dictionary<string, Hash128>();
for (int i = AllDependencies.Length - 1; i >= 0; i--)
{
dicDownloadInfos.Add(AllDependencies[i], assetBundleManifestServer.GetAssetBundleHash(AllDependencies[i]));
}
dicDownloadInfos.Add(AssetName, assetBundleManifestServer.GetAssetBundleHash(AssetName));
if (assetBundleManifestServer != null) //依赖文件不为空的话下载依赖文件
{
Debug.Log("Hash:" + assetBundleManifestServer.GetHashCode());
dicDownloadInfos.Add(RootAssetsName, new Hash128(0, 0, 0, 0));
}
//卸载掉,无法同时加载多个配置文件
ServerManifestWWW.assetBundle.Unload(true);
if (File.Exists(saveLocalPath + "/" + RootAssetsName))
{
LocalManifestAssetBundle = AssetBundle.LoadFromFile(saveLocalPath + "/" + RootAssetsName);
assetBundleManifestLocal = LocalManifestAssetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
foreach (var item in dicDownloadInfos)
{
if (!CheckLocalFileNeedUpdate(item.Key, item.Value, RootAssetsName, saveLocalPath, assetBundleManifestLocal))
{
Debug.Log("无需下载:" + item.Key);
continue;
}
else
{
DeleteFile(saveLocalPath + "/" + item.Key);
}
//直接加载所有的依赖项就好了
WWW wwwAsset = new WWW(AssetsHost + "/" + item.Key);
//获取加载进度
while (!wwwAsset.isDone)
{
Debug.Log(string.Format("下载 {0} : {1:N1}%", item.Key, (wwwAsset.progress * 100)));
yield return new WaitForSeconds(0.2f);
}
//保存到本地
SaveAsset2LocalFile(saveLocalPath, item.Key, wwwAsset.bytes, wwwAsset.bytes.Length);
}
if (LocalManifestAssetBundle != null)
{
LocalManifestAssetBundle.Unload(true);
}
if (OnDownloadOver != null)
{
OnDownloadOver();
}
}
/// <summary>
/// 检测本地文件是否存在已经是否是最新
/// </summary>
/// <param name="AssetName"></param>
/// <param name="RootAssetsName"></param>
/// <param name="localPath"></param>
/// <param name="serverAssetManifestfest"></param>
/// <param name="CheckCount"></param>
/// <returns></returns>
bool CheckLocalFileNeedUpdate(string AssetName, Hash128 hash128Server, string RootAssetsName, string localPath, AssetBundleManifest assetBundleManifestLocal)
{
Hash128 hash128Local;
bool isNeedUpdate = false;
if (!File.Exists(localPath + "/" + AssetName))
{
return true; //本地不存在,则一定更新
}
if (!File.Exists(localPath + "/" + RootAssetsName)) //当本地依赖信息不存在时,更新
{
isNeedUpdate = true;
}
else //总的依赖信息存在切文件已存在 对比本地和服务器两个文件的Hash值
{
if (hash128Server == new Hash128(0, 0, 0, 0))
{
return true; //保证每次都下载总依赖文件
}
hash128Local = assetBundleManifestLocal.GetAssetBundleHash(AssetName);
//对比本地与服务器上的AssetBundleHash 版本不一致就下载
if (hash128Local != hash128Server)
{
isNeedUpdate = true;
}
}
return isNeedUpdate;
}
/// <summary>
/// 非递归式加载指定AB,并加载依赖项,并返回目标GameObject
/// </summary>
/// <param name="RootAssetsName"></param>
/// <param name="AssetName"></param>
/// <param name="LocalPath"></param>
public GameObject GetLoadAssetFromLocalFile(string RootAssetsName, string AssetName, string PrefabName, string LocalPath)
{//这里还没有对没用到的资源进行释放。
AssetBundle assetBundle = AssetBundle.LoadFromFile(LocalPath + "/" + RootAssetsName);
AssetBundleManifest assetBundleManifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
string[] AllDependencies = assetBundleManifest.GetAllDependencies(AssetName);
for (int i = AllDependencies.Length - 1; i >= 0; i--)
{
AssetBundle assetBundleDependencies = AssetBundle.LoadFromFile(LocalPath + "/" + AllDependencies[i]);
assetBundleDependencies.LoadAllAssets();
}
print(LocalPath + "/" + AssetName);
AssetBundle assetTarget = AssetBundle.LoadFromFile(LocalPath + "/" + AssetName);
return assetTarget.LoadAsset<GameObject>(PrefabName);
}
/// <summary>
/// 递归加载本地所有依赖项
/// </summary>
/// <param name="RootAssetsName"></param>
/// <param name="AssetName"></param>
/// <param name="LocalPath"></param>
AssetBundleManifest assetBundleManifestLocalLoad; //递归加载时候用
public void RecursionLoadAssetFromLocalFile(string RootAssetsName, string AssetName, string LocalPath, int RecursionCounter)
{
if (RecursionCounter++ == 0)
{
//加载本地Manifest获取依赖项
assetBundleManifestLocalLoad = AssetBundle.LoadFromFile(LocalPath + "/" + RootAssetsName).LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
//当前AssetName所有依赖项
string[] AllDependencies = assetBundleManifestLocalLoad.GetDirectDependencies(AssetName);
for (int i = 0; i < AllDependencies.Length; i++)
{
RecursionLoadAssetFromLocalFile(RootAssetsName, AllDependencies[i], LocalPath, RecursionCounter);
}
AssetBundle assetBundle = AssetBundle.LoadFromFile(LocalPath + "/" + AssetName);
assetBundle.LoadAllAssets();
}
/// <summary>
/// 将文件模型创建到本地
/// </summary>
/// <param name="path"></param>
/// <param name="name"></param>
/// <param name="info"></param>
/// <param name="length"></param>
void SaveAsset2LocalFile(string path, string name, byte[] info, int length)
{
Stream sw = null;
FileInfo fileInfo = new FileInfo(path + "/" + name);
if (fileInfo.Exists)
{
fileInfo.Delete();
}
//如果此文件不存在则创建
sw = fileInfo.Create();
//写入
sw.Write(info, 0, length);
sw.Flush();
//关闭流
sw.Close();
//销毁流
sw.Dispose();
Debug.Log(name + "成功保存到本地~");
}
/// <summary>
/// 删除文件
/// </summary>
/// <param name="path"></param>
void DeleteFile(string path)
{
File.Delete(path);
}
}
测试代码:
这里是从本地文件夹下进行下载与加载的,所以前面加上file://
要注意调用函数的时候,下载时第一个是下载路径,然后是总依赖关系,文件名(要带拓展名),保存路径,下载后要执行的无参的函数。
加载时参数是总依赖关系,文件名(要带拓展名),预制体名,保存AB包的路径
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class text : MonoBehaviour
{
void Start()
{
//5.0版本打包时候选中需要打包的东西然后设置右下角名称,同个/设置多集目录,后面的框标记后缀(后缀不重要)
//打包时候的目标文件夹,假设目标文件夹名称为"WJJ",那么会生成"WJJ"和"WJJ.manifest"两个文件
//其中WJJ.manifest文件没有用,只是用来看的,WJJ是一个assetbundle包,里面包含了整个文件夹的依赖信息
//可以先加载这个东西,然后获取到依赖关系后逐步加载
string savePath = Application.persistentDataPath;
print(savePath);
string path = "file://"+Application.dataPath;
print(path);
try
{
LoadAssetBundle.Instance.DownLoadAssets2LocalWithDependencies(path+"/BuildABs", "BuildABs", "cube.u3d", savePath, () =>
{
GameObject obj = LoadAssetBundle.Instance.GetLoadAssetFromLocalFile("BuildABs", "cube.u3d", "Cube", Application.persistentDataPath);
GameObject.Instantiate(obj);
print(obj.name);
//obj.GetComponent<Renderer>().sharedMaterial.shader = Shader.Find(obj.GetComponent<Renderer>().sharedMaterial.shader.name);
});
}
catch (Exception ex)
{
Debug.Log(ex.Message);
}
}
自动设置AssetBundle名称:只要资源设置了AssetBundle名称,BuildPipeline.BuildAssetBundles函数就可以将此资源打包。但是如果一个模型Model带有一个材质Mat,只把Model设置了AssetBundle名称,打包时Mat将会被一起打包到Model的资源包中,这就产生了一个问题,如果多个模型公用一个Mat,那么Mat就会被重复打包进多个资源中,因此建议将公用的资源(材质、贴图等等)全部设置AssetBundle名称,从而进行分开打包,而模型单独使用的资源可以打包到一起。那么涉及到如果项目有几百甚至更多的资源需要打包的话,手动设置AssetBundle名称会太费时,因此可以将打包的资源放置在一个文件夹中,然后通过AssetDatabase.GetDependencies()函数来获取某个模型的依赖资源、AssetImporter.GetAtPath()获取资源的打包信息、AssetDatabase.AssetPathToGUID()来获取资源的哈希ID,然后调用AssetImporter.assetBundleName属性,用资源的哈希ID设置依赖资源的AssetBundle名称,最后再分离打包。注意:打包前一定要将AssetBundle的名称先清以下,防止重复打包。
如何使用Assetsbundle打包,下载,加载