AssetBandle打包和加载

本篇说的是关于AssetBundle打包的几种方式和加载方式,包括依赖打包和网络下载等,学习的时候注释写得很详细,在此记录方便自己查询,也希望能够帮到需要的朋友。(AssetBundle以下会简称AB)

1.打包AB资源的脚本

using UnityEditor;
using System.IO;
using UnityEngine;

public class CreateAssetBundle
{
    /// <summary>
    /// 打包AB,Unity会将具有相同标签的资源打成一个包
    /// </summary>
    [MenuItem("Assets/CreateAB #&c")]
    static void CreateAB()
    {
        //判断是否存在文件存储的目录文件,没有就创建
        if (!Directory.Exists(DirectoryFileStr.fileName))
        {
            Directory.CreateDirectory(DirectoryFileStr.fileName);
            AssetDatabase.Refresh();
        }

        //这种打包模式下,Unity会自动检测所有带有打包标签的资源,然后打包(打包的是所有设置了标签的资源),没设置当然就不打包了
        //设置的位置:以资源形式查看的Inspector面板最下面,看不到的时候将上拉框拉上去
        //正式打包,参数:打包后存储的文件路径,打包选项(选择压缩方式),打包文件所使用的平台
        BuildPipeline.BuildAssetBundles(DirectoryFileStr.fileName, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log("打包成功");
    }
}

2.加载AB的脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.Networking;

/// <summary>
/// 定义一些特定的文件目录,此类如果定义在Editor所在的文件夹下,那么在非Editor文件夹下的类中是无法访问的
/// </summary>
public class DirectoryFileStr
{
    /// <summary>
    /// 打包文件存储的总目录
    /// </summary>
    public const string fileName = "Assets/StreamingAssets/";

    public const string onAssetsFile = "/StreamingAssets/";
}

public class LoadAB : MonoBehaviour {

	// Use this for initialization
	void Start () {
        #region 使用AssetBundle.LoadFromFile(string path)从本地同步加载AB包,加载的时候会等待加载完毕,主线程会暂停

        //1.预设CubeWall和CapsuleWall打包标签都为wall.unity3d,只会生成一个AB包
        //打包形式:会将两个预设打成一个包,打包出来的文件总体较小,因为打成一个包,会将他们使用的相同材质只打包一次(包文件中材质占比很大),然后共享材质数据
        //使用时机:资源(预设等)使用了相同的材质并且在场景中需要同时加载并实例化的时候可以
        //AssetBundle ab = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "scene/wall.unity3d");//加载AB包,必须使用完整路径
        //GameObject CubeWall = ab.LoadAsset<GameObject>("CubeWall");//加载Asset资源,名字必须与预设体名字相同
        //GameObject CapsuleWall = ab.LoadAsset<GameObject>("CapsuleWall");
        //Instantiate(CubeWall);//实例化加载的资源,实例化后才会出现在场景中,不然只会存在于内存中
        //Instantiate(CapsuleWall);

        //2.预设CubeWall和CapsuleWall的打包标签分别设置(打包的时候不用根据不同标签分别处理,Unity只管有没有设置打包标签),会生成两个AB包
        //打包形式:会根据不同的打包标签生成不同的AB包,打包出来的文件总体较大,因为每个包中都包含了材质的数据
        //使用时机:资源使用了不同的材质、在场景中不需要同时加载并实例化(一次加载太多消耗大又费时)
        //AssetBundle abCapsule = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "scene/capsule_wall.unity3d");//加载AB包
        //AssetBundle abCube = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "scene/cube_wall.unity3d");
        //GameObject CapsuleWall = abCapsule.LoadAsset<GameObject>("CapsuleWall");//加载Asset资源
        //GameObject CubeWall = abCube.LoadAsset<GameObject>("CubeWall");
        //Instantiate(CubeWall);//实例化加载的资源,实例化后才会出现在场景中,不然只会存在于内存中
        //Instantiate(CapsuleWall);

        //3.依赖打包:预设CubeWall和CapsuleWall的打包标签分别设置,将资源中共享的部分分离出来单独打包(例如两个预设使用了相同的材质),会生成3个AB包
        //打包形式:Unity会以很小的代价记录资源和依赖项的依赖关系(预设和材质),在加载的时候自动处理,打包出来的文件总体较小
        //使用时机:一些资源中有相同的部分,但是这些资源又不会在同一个时机加载和实例化
        //使用注意:在加载资源之前要先加载所依赖的资源,不然会造成材质丢失
        //AssetBundle shareWall = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "share/wall.unity3d");//使用依赖项只需要加载AB包即可
        //AssetBundle abCapsule = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "scene/capsule_wall.unity3d");//加载AB包
        //AssetBundle abCube = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "scene/cube_wall.unity3d");
        //GameObject CapsuleWall = abCapsule.LoadAsset<GameObject>("CapsuleWall");//加载Asset资源
        //GameObject CubeWall = abCube.LoadAsset<GameObject>("CubeWall");
        //Instantiate(CubeWall);//实例化加载的资源,实例化后才会出现在场景中,不然只会存在于内存中
        //Instantiate(CapsuleWall);

        //Material wallMat = shareWall.LoadAsset<Material>("Wood texture floor_01");//单独加载出来的材质可以赋值给其他对象使用
        //GameObject objTest = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        //objTest.name = "SphereWall";
        //objTest.transform.position = new Vector3(-5, 0, 0);
        //objTest.transform.localScale = Vector3.one * 2;
        //objTest.GetComponent<Renderer>().material = wallMat;

        #endregion

        #region 使用AssetBundle.LoadFromMemoryAsync(byte[] bytes)从内存中异步加载,要使用协程,主线程不会暂停

        //StartCoroutine(AsyncLoadAB(DirectoryFileStr.fileName + "scene/capsule_wall.unity3d"));

        #endregion

        #region 使用AssetBundle.LoadFromMemory(byte[] bytes)从内存中同步加载,加载的时候会等待加载完毕,主线程会暂停

        //AssetBundle.LoadFromMemory(File.ReadAllBytes(DirectoryFileStr.fileName + "share/wall.unity3d"));//使用依赖项只需要加载AB包即可,这里使用的是同步加载
        //AssetBundle abM = AssetBundle.LoadFromMemory(File.ReadAllBytes(DirectoryFileStr.fileName + "scene/capsule_wall.unity3d"));//会暂停主线程等待加载完毕

        加载完毕后使用
        //GameObject CapsuleWall = abM.LoadAsset<GameObject>("CapsuleWall");//加载Asset资源
        //Instantiate(CapsuleWall);//实例化加载的资源,实例化后才会出现在场景中,不然只会存在于内存中

        #endregion

        #region 使用WWW.LoadFromCacheOrDownload从内存或者服务器下载AB包,只有第一次才会下载,然后缓存起来,今后再次执行的时候回使用之前缓存下来的数据

        //1.从本地加载
        //以下3种都行,说明路径最好用正斜杠,从本地下载需要全路径到根目录,并且根目录之前需要加上file://;从服务器下载要使用http://开头
        //Caching.CleanCache();//清除缓存,避免上次成功加载的数据因为没有清除而影响测试
        //StartCoroutine(WWWLoadAB(@"file://G:/Study/Unity/AssetBundle_Study/" +  DirectoryFileStr.fileName + "share/wall.unity3d", @"file://G:/Study/Unity/AssetBundle_Study/" + DirectoryFileStr.fileName + "scene/cube_wall.unity3d"));
        //StartCoroutine(WWWLoadAB("file://G:/Study/Unity/AssetBundle_Study/" + DirectoryFileStr.fileName + "share/wall.unity3d", "file://G:/Study/Unity/AssetBundle_Study/" + DirectoryFileStr.fileName + "scene/cube_wall.unity3d"));
        //StartCoroutine(WWWLoadAB("file://" + Application.dataPath + DirectoryFileStr.onAssetsFile + "share/wall.unity3d", "file://" + Application.dataPath + DirectoryFileStr.onAssetsFile + "scene/cube_wall.unity3d"));

        //2.从服务器加载(涉及到服务器的搭建,直接双击AssetBundle_Study_WebServer/NetBox2.exe就是开启服务器,拖动资源文件夹到.exe同级目录下就可以下载了,localhost是NetBox2.exe自带的网址根目录)
        //Caching.CleanCache();//清除缓存,避免上次成功加载的数据因为没有清除而影响测试
        //StartCoroutine(WWWLoadAB(@"http://localhost" + DirectoryFileStr.onAssetsFile + "share/wall.unity3d", @"http://localhost" + DirectoryFileStr.onAssetsFile + "scene/cube_wall.unity3d"));

        #endregion

        #region 使用UnityWebRequest从本地或者服务器下载资源,本方法即将取代要被弃用的WWW.LoadFromCacheOrDownload方法

        //Caching.CleanCache();//清除缓存,避免上次成功加载的数据因为没有清除而影响测试
        //StartCoroutine(UnityWebRequestLoadAB("file://" + Application.dataPath + DirectoryFileStr.onAssetsFile + "share/wall.unity3d", "file://" + Application.dataPath + DirectoryFileStr.onAssetsFile + "scene/cube_wall.unity3d"));

        #endregion

        #region 使用manifest文件加载资源

        //AssetBundle manifestAB = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "StreamingAssets");//路径下的这个文件包含了所有的AB包的概要信息,只要打AB包就会生成
        //AssetBundleManifest manifest = manifestAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");//这里的参数是固定的填写这个

        获得所有AB包名
        //string[] allAssetStrs = manifest.GetAllAssetBundles();
        打印所有AB包名
        //foreach (var resABName in allAssetStrs)
        //{
        //    print(resABName);
        //    //也可以在此加载所有的AB包然后实例化
        //}

        获得一个AB包所有的依赖包,这个AB包可以通过上一步获得,这里为了方便填写一个已知的
        //string[] strs = manifest.GetAllDependencies("scene/cube_wall.unity3d");
        //print("依赖包个数:" + strs.Length);
        打印这个AB包所有依赖的包名
        //foreach (var relyABName in strs)
        //{
        //    print("依赖包");
        //    print(relyABName);
        //    //也可以在这里加载所有的依赖包
        //}

        #endregion

        #region AssetBundle的卸载

        //AssetBundle shareWall = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "share/wall.unity3d");//使用依赖项只需要加载AB包即可
        //AssetBundle abCapsule = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "scene/capsule_wall.unity3d");//加载AB包
        //AssetBundle abCube = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "scene/cube_wall.unity3d");
        //GameObject CapsuleWall = abCapsule.LoadAsset<GameObject>("CapsuleWall");//加载Asset资源
        //GameObject CubeWall = abCube.LoadAsset<GameObject>("CubeWall");
        //Instantiate(CubeWall);//实例化加载的资源,实例化后才会出现在场景中,不然只会存在于内存中
        //Instantiate(CapsuleWall);

        StartCoroutine(UnLoadAB(shareWall));//卸载依赖的材质会导致场景中使用这个材质的对象丢失材质
        //StartCoroutine(UnLoadAB(abCapsule));//卸载AB包在内存中所占的内存,但不会销毁已经实例化出来的对象

        #endregion


    }

    IEnumerator AsyncLoadAB(string path)
    {
        yield return AssetBundle.LoadFromFileAsync(DirectoryFileStr.fileName + "share/wall.unity3d");//LoadFromFileAsync从本地异步加载AB包的方法
        //加载AB
        AssetBundleCreateRequest abCR = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
        yield return abCR;//在协程程序中等待加载完成而不影响主线程,这就是协程异步加载的价值所在,而直接使用LoadFromMemory同步加载,会暂停主线程等待加载完毕

        //加载完毕后使用
        GameObject CapsuleWall = abCR.assetBundle.LoadAsset<GameObject>("CapsuleWall");//加载Asset资源
        Instantiate(CapsuleWall);//实例化加载的资源,实例化后才会出现在场景中,不然只会存在于内存中
    }

    IEnumerator WWWLoadAB(string pathShare, string pathWall)
    {
        //如果缓存系统没有准备好就等待
        while(Caching.ready == false)
        {
            yield return null;
        }

        WWW wwwShare = WWW.LoadFromCacheOrDownload(pathShare, 1);//参数:路径或者网址、版本号
        yield return wwwShare;

        //使用WWW加载资源如果有错误,它本身是不会报错的,需要我们自己判断
        if (string.IsNullOrEmpty(wwwShare.error) == false)
        {
            Debug.Log(wwwShare.error);
            yield break;//跳出协程,及结束协程
        }

        WWW wwwWall= WWW.LoadFromCacheOrDownload(pathWall, 1);//参数:路径或者网址、版本号
        yield return wwwWall;

        //使用WWW加载资源如果有错误,它本身是不会报错的,需要我们自己判断
        if (string.IsNullOrEmpty(wwwWall.error) == false)
        {
            Debug.Log(wwwWall.error);
            yield break;//跳出协程,及结束协程
        }

        GameObject obj = wwwWall.assetBundle.LoadAsset<GameObject>("CubeWall");
        Instantiate(obj);
    }

    IEnumerator UnityWebRequestLoadAB(string pathShare, string pathWall)
    {
        UnityWebRequest requestShare = UnityWebRequest.GetAssetBundle(pathShare);//先根据路径获取一个网络请求对象
        yield return requestShare.Send();//网络请求对象发送消息正式请求下载,并等待下载完成
        DownloadHandlerAssetBundle.GetContent(requestShare);//使用AB下载处理器获取下载的类容,直接获取AB对象

        UnityWebRequest requestScene = UnityWebRequest.GetAssetBundle(pathWall);//先根据路径获取一个网络请求对象
        yield return requestScene.Send();//网络请求对象发送消息正式请求下载,并等待下载完成
        AssetBundle webSceneWall = DownloadHandlerAssetBundle.GetContent(requestScene);//使用AB下载处理器获取下载的类容,直接获取AB对象

        //使用获得的AB对象
        GameObject webWall = webSceneWall.LoadAsset<GameObject>("CubeWall");
        Instantiate(webWall);
    }

    /// <summary>
    /// 卸载AB资源,卸载不代表会销毁场景中的对象,只是从内存中销毁了,不能再依靠内存中的AB实例化对象了,要想再次实例化必须再加载一次到内存
    /// </summary>
    /// <param name="ab"></param>
    /// <returns></returns>
    IEnumerator UnLoadAB(AssetBundle ab)
    {
        yield return new WaitForSeconds(3);//等待3秒
        ab.Unload(true);//参数为true,卸载当前AB包中所有资源,即使有资源被使用着(最好在切换关卡和场景或者资源没有被使用的时候调用)

        ab.Unload(false);//参数为false,卸载当前AB包中所有没有被使用的资源

        //个别资源怎么卸载(没法获取资源所在的包的时候)?
        //调用Resources.UnloadUnusedAssets();卸载所有没有被使用的资源(这个函数在切换场景的时候回自动调用)
    }
}

最近注意到加载AB包中的资源可以有不同的路径传入方式,总结如下代码所示:

        //加载AB名字必须与预设体名字相同
        //直接名字:都可以
        //AssetBundle ab = AssetBundle.LoadFromFile(DirectoryFileStr.fileName + "ab_cube.unity3d");//加载AB包,使用Assets下的相对路径可以
        AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/ab_cube.unity3d");//加载AB包,使用绝对路径也可以
        //GameObject CubeWall = ab.LoadAsset<GameObject>("Cube");//直接预设名字没有后缀也可以
        //GameObject CubeWall = ab.LoadAsset<GameObject>("Cube.prefab");//直接预设名字加后缀也可以
        //局部路径:带后缀的可以
        GameObject CubeWall = ab.LoadAsset<GameObject>("Assets/Prefabs/Cube.prefab");//Assets目录下的局部路径带后缀也可以(这里的路径为资源在打包之前所在的路径)
        //GameObject CubeWall = ab.LoadAsset<GameObject>("Assets/Prefabs/Cube");//Assets目录下的局部路径没有后缀不可以
        //绝对路径:都不可以
        //GameObject CubeWall = ab.LoadAsset<GameObject>(Application.dataPath + "/Prefabs/Cube");//全路径没有后缀也不可以
        //GameObject CubeWall = ab.LoadAsset<GameObject>(Application.dataPath + "/Prefabs/Cube.prefab");//全路径有后缀也不可以
        Instantiate(CubeWall);//实例化加载的资源,实例化后才会出现在场景中,不然只会存在于内存中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值