Unity AssetBundle详细解析

AssetBundle使用

1.1 AssetBundle介绍

AssetBundle是将资源使用Unity提供的一种用于存储资源的压缩格式打包后的集合,它可以存储任何一种Unity可以识别的资源,如模型,纹理图,音频,场景等资源。也可以加载开发者自定义的二进制文件。他们的文件类型是.assetbundle或者

/.unity3d,他们先前被设计好,很容易就下载到我们的游戏或者场景当中。

AssetBundle(简称AB包)是一个资源压缩包,包含模型、贴图、预制体、声音、甚至整个场景,可以在游戏运行的时候被加载;

基本特点如下所示:

1AssetBundle自身保存着互相的依赖关系;

2压缩包可以使用LZMA和LZ4压缩算法,减少包大小,更快的进行网络传输;

3把一些可以下载内容放在AssetBundle里面,可以减少安装包的大小;如果所有的资源文件,全部打包到程序中,那么程序的安装包就会很大

(4)AssetBundle 文件放在服务器上,用的时候再从服务器进行加载,所以这个包根本就不在程序当中

一般情况下AssetBundle的具体开发流程如下:

(1)创建Asset bundle,开发者在unity编辑器中通过脚本将所需要的资源打包成AssetBundle文件。

(2)上传服务器,开发者将打包好的AssetBundle文件上传至服务器中。使得游戏客户端能够获取当前的资源,进行游戏的更新。

(3)下载AssetBundle,首先将其下载到本地设备中,然后再通过AsstBudle的加载模块将资源加到游戏之中。

(4)加载,通过Unity提供的API可以加载资源里面包含的模型、纹理图、音频、动画、场景等来更新游戏客户端。

(5)卸载AssetBundle,卸载之后可以节省内存资源,并且要保证资源的正常更新。

 

1.2 AssetBundle打包

1:我们首先自己进行打包,打包方式如下。新建Editor目录,写如下脚本,不同平台指定不同的目录以及BuildTarget不同即可。

参数含义:路径+选项+平台

-- Build的路径:自己定义路径即可,也可以是StreamingAssets路径

--BuildAssetBundleOptions

BuildAssetBundleOptions.None:使用LZMA算法压缩,压缩的包更小,但是加载时间更长。使用之前需要整体解压。一旦被解压,这个包会使用LZ4重新压缩。使用资源的时候不需要整体解压。在下载的时候可以使用LZMA算法,一旦它被下载了之后,它会使用LZ4算法保存到本地上。

BuildAssetBundleOptions.UncompressedAssetBundle:不压缩,包大,加载快

BuildAssetBundleOptions.ChunkBasedCompression:使用LZ4压缩,压缩率没有LZMA高,但是我们可以加载指定资源而不用解压全部。

注意:使用LZ4压缩,可以获得可以跟不压缩想媲美的加载速度,而且比不压缩文件要小。

平台:直接查看即可,各种平台

 

public class ExportAssets : MonoBehaviour

{

    [@MenuItem("AssetBundle/Build Window Bundles")]

    static void BuildAssetBundles()

    {

        //使用BuildPipeline进行打包:路径+选项+平台

        BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath,

            BuildAssetBundleOptions.UncompressedAssetBundle,

            BuildTarget.StandaloneWindows);

}

 

[@MenuItem("AssetBundle/Build AssetBundles")]

    static void BuildAssetBundles()

    {

        string dir = "AssetBundles";

        if (Directory.Exists(dir) == false)

        {

            Directory.CreateDirectory(dir);

        }

        //BuildTarget 选择build出来的AB包要使用的平台

        BuildPipeline.BuildAssetBundles(dir,BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);

    }

}

 

命名规范:

1、必须小写字母,就算你大写了Unity也会自动转小写

2、命名规范:名称后要加 .unity3d 或者 .assetbundle

3、名称+“/”+另一个名称,会自动生成子选项(打包时,会自动生成一个新的名称文件夹)

 

打包之后,会生成对应的后缀为manifest的文件。什么是Manifest文件?

- CRC为校验码,通过其检查是否完整
- Assets 表示包里包含多少资源
- Dependencies 表示包有哪些依赖
注意:在加载这些包之前,也需要加载依赖的包,不然会丢失这部分内容,显示效果不正确

 

2:下面演示AssetBundle的下载,如下代码所示,使用Python开启简易文件服务器

Python 2.x版本:python -m SimpleHTTPServer 8080

Python 3.x版本:python -m http.server 8080

 

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class DownAsset : MonoBehaviour

{

    public GameObject cube;

    public GameObject sphere;

    public Transform newPos;

 

    private string url1 = "http://127.0.0.1:8080/cubeasset"; //立方体

    private string url2 = "http://127.0.0.1:8080/image";     //图片

    private string url3 = "http://127.0.0.1:8080/red";     //材质

    // Start is called before the first frame update

    void Start()

    {

      //  StartCoroutine(downloadTexture(url2));

       // StartCoroutine(downloadMat(url3));

        StartCoroutine(downloadPrefab(url1));

    }

    IEnumerator downloadTexture(string url) {

        //WWW是网络功能类:可以使用UnityWebRequest进行替换

        WWW data = new WWW(url);

        yield return data;

 

        //从内存直接加载,因为WWW已经将数据下载下来了,参数是字节

       // AssetBundle bundle = AssetBundle.LoadFromMemory(data.bytes);

        //从Bundle根据名称加载

       // GetComponent<Renderer>().material.mainTexture = (Texture)bundle.LoadAsset("LEVELSELECTION_9");

 

        //从下面直接加载

        GetComponent<Renderer>().material.mainTexture = (Texture)data.assetBundle.LoadAsset("LEVELSELECTION_9");

 

        //.参数为false时,bundle内的序列化数据将被释放,但是任何从这个bundle中实例化的物体都将完好,所以不能从这个bundle中加载其他物体

        //2.参数为true时,所有从该bundle中加载的物体也将被销毁,如果场景中有物体引用该资源,引用会丢失

        // bundle.Unload(false);

    }

 

    IEnumerator downloadMat(string url) {

        WWW downAsset = new WWW(url);

        yield return downAsset;

 

        sphere.GetComponent<Renderer>().material =(Material) downAsset.assetBundle.LoadAsset("Red");

        downAsset.assetBundle.Unload(false);

 

        downAsset.Dispose();

 

    }

 

    IEnumerator downloadPrefab(string url) {

        WWW downAsset = new WWW(url);

        yield return downAsset;

        GameObject goPrefab = (GameObject)Instantiate(downAsset.assetBundle.LoadAsset("Cube"));

        goPrefab.GetComponent<MeshRenderer>().material.color = Color.yellow;

        goPrefab.transform.position = newPos.transform.position;

 

        //true与false区别:株连九族与一人做事一人当的区别

        downAsset.assetBundle.Unload(false);

 

    }

}

 

1.3 AssetBundle Browser

官方提供的AssetBundle Browser打包工具,好用速度快。大家直接在Window-->Package Manager中进行查找AssetBundle Browser,进行安装即可。

下载完成之后,Window-->AssetBundle Browser,界面异常简单,大家自己操作即可。

不仅仅可以查看项目所有的AssetBundle,也可以分析其中的依赖关系。出现重复依赖的时候,会给大家提示叹号警告。

Build选项下,可以根据不同的平台进行打包,非常方便。

 

1.4  AB加载与卸载

AssetBundle常见函数如下

 

AssetBundle.LoadFromFile 从本地同步加载

AssetBundle.LoadFromMemory 从内存加载

AssetBundle.LoadFromFileAsync 从本地异步加载

AssetBundle.LoadAsset(assetName)从AB中根据名称加载资源

AssetBundle.LoadAllAssets() 加载AB包中所有的对象,不包含依赖的包

AssetBundle.LoadAssetAsync() 异步加载,加载较大资源的时候

AssetBundle.LoadAllAssetsAsync() 异步加载全部资源

AssetBundle.LoadAssetWithSubAssets 加载资源及其子资源

 

AB的卸载,减少内存的使用,有可能导致丢失,在切换场景,或者确定不使用的时候卸载

AssetBundle.Unload(true) 卸载所有资源,包含其中正被使用的资源

AssetBundle.Unload(false) 卸载AssetBundle的资

Resources.UnloadUnusedAssets 卸载个别未使用的资源

 

 

 AssetBundle分组策略总结

(1)逻辑实体分组

一个UI界面或者所有UI界面一个包(这个界面里面的贴图和布局信息一个包)

所有的场景所共享的部分一个包(包括贴图和模型)

(2)按照类型分组

所有声音资源打成一个包,所有预设体打成一个包,所有材质打成一个包

(3)按照使用分组

可以按照场景分,一个场景所需要的资源一个包

 

 

 

1.5 AssetBudle加载示例

(1)使用协程加载

 //经过测试,Win打包可用,安卓打包也可用

    //安桌下Application.streamingAssetsPath其实就等于

    //“jar:file://” + Application.dataPath + “!/assets/”,不需要再添加file:///;

    IEnumerator LoadAssetBundleByWWW()

    {

        string localPath = "";

        if (Application.platform == RuntimePlatform.Android)

        {

            localPath = Application.streamingAssetsPath + "/" + "image.assetbundle";

        }

        else

        {

            localPath = "file:///" + Application.streamingAssetsPath + "/" + "image.assetbundle";

        }

 

        WWW data = new WWW(localPath);

        yield return data;

        Texture mat = (Texture)data.assetBundle.LoadAsset("ds");

        GetComponent<MeshRenderer>().material.mainTexture = mat;

}

(2)使用LoadFromFile加载

   void LoadFromDataPath()

    {

        //一般的文件,放到StreamingAsset目录下的时候,使用WWW进行读取

        //但是,AssetBundle放在这个目录的时候,不仅仅可以使用WWW,

        //还可以使用自带的LoadFromFile函数进行读取

        //直接放到StreamingAssets目录下,经过测试安卓和Win打包都成功加载

        AssetBundle bundle =   AssetBundle.LoadFromFile(Application.streamingAssetsPath+ "/image.assetbundle");

         GetComponent<Renderer>().material.mainTexture =(Texture)bundle.LoadAsset("ds");

     }

 

 

1.6 使用UnityWebRequest替代WWW

新版本中,www已经被UnityWebRequest替代,使用方式如下,大家进行替换即可。如果不想使用协程进行下载,就是用while循环判断请求isDone即可。

    IEnumerator WebServer() {

        const string url1 = @"http://127.0.0.1:8080/mat";     

        const string url2 = @"http://127.0.0.1:8080/image";

        UnityWebRequest request1 = UnityWebRequestAssetBundle.GetAssetBundle(url1);  //Unity网络请求AssetBundle.获取资源(网络地址1)

        UnityWebRequest request2 = UnityWebRequestAssetBundle.GetAssetBundle(url2);  //传入地址2

        yield return request1.SendWebRequest();                                       //发送Web请求1

        yield return request2.SendWebRequest();                                       //发送web请求2

        AssetBundle ab1 = DownloadHandlerAssetBundle.GetContent(request1);             //下载资源委托,获取连接请求1,返回AssetBundle资源

        AssetBundle ab2 = DownloadHandlerAssetBundle.GetContent(request2);              //获取连接请求2,返回AssetBundle资源

   

        //获取AsseBundle资源,可以做任意事情

//ab1加载代码,自己实现即可

    }

 

1.7 AssetBundle依赖管理

案例前提条件:

创建了一个材质,材质上有一张贴图,将该材质打包成mat.assetbundle。

将材质依赖的贴图ds.png,打包成image.assetbundle。

我们使用LoadFromFile加载材质的时候,如果不先加载贴图,就会丢失贴图。所以,我们需要提前加载该依赖文件。

public class LoadDependency : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

        LoadModelFromFile();

    }

    //AssetBundle依赖管理,不同平台产生的依赖文件名字不一样

    void LoadModelFromFile()

    {

        //加载主配置文件不同平台不一样

        AssetBundle bundle = AssetBundle.LoadFromFile(

            Application.streamingAssetsPath + "/StandaloneWindows");

        

        AssetBundleManifest manifest = bundle.LoadAsset<AssetBundleManifest>

            ("AssetBundleManifest");

 

//获取mat对应的依赖关系

        string[] dependencies = manifest.GetAllDependencies("mat.assetbundle");

        //依次加载依赖文件

        foreach (string dependency in dependencies)

        {

            AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + dependency);

        }

        //因为存在依赖关系,所以bundle必须加载被依赖的资源

        AssetBundle bundle1 = AssetBundle.LoadFromFile(Application.streamingAssetsPath

            + "/mat.assetbundle");

//Red是材质的名称,大家直接根据自己的进行变化即可

       GetComponent<Renderer>().material=  bundle1.LoadAsset<Material>("Red");

        bundle1.Unload(false);

 

    }

}

 

 

1.8:AssetBundle加载方式代码

using System;

using System.Collections;

using System.Collections.Generic;

using System.IO;

using UnityEngine;

using UnityEngine.Networking;

using UnityEngine.UI;

public class LoadFromFile : MonoBehaviour

{

    public Text pathName;

    public Text streamingPath;

    // Start is called before the first frame update

    void Start()

    {

       // pathName.text = Application.dataPath;

       // streamingPath.text = Application.streamingAssetsPath;

        //  LoadFromDataPath();

        //  StartCoroutine(LoadAssetBundleByWWW());

        StartCoroutine(SendRequest());

    }

    //运行时候判断平台

    void LoadFromDataPath() {

        //Application.dataPath+文件名为什么不能读取:因为项目打包的时候将Asset目录下的资源进行了压缩,无法直接找到对应的资源

        //一般的文件,放到StreamingAsset目录下的时候,使用WWW进行读取

        //但是,AssetBundle放在这个目录的时候,不仅仅可以使用WWW,还可以使用自带的LoadFromFile函数进行读取

        

   //测试安卓与win都通过

        AssetBundle bundle =   AssetBundle.LoadFromFile(Application.streamingAssetsPath+"/image");

         GetComponent<Renderer>().material.mainTexture = (Texture)bundle.LoadAsset("LEVELSELECTION_9");

         

    }

 

 

 

     //经过测试,Win打包可用,安卓打包也可用

    //安桌下Application.streamingAssetsPath其实就等于

    //“jar:file://” + Application.dataPath + “!/assets/”,不需要再添加file:///;

    IEnumerator LoadAssetBundleByWWW()

    {

        string localPath = "";

        if (Application.platform == RuntimePlatform.Android)

        {

            localPath = Application.streamingAssetsPath + "/" + "image.assetbundle";

        }

        else

        {

            localPath = "file:///" + Application.streamingAssetsPath + "/" + "image.assetbundle";

        }

        WWW data = new WWW(localPath);

        yield return data;

        Texture mat = (Texture)data.assetBundle.LoadAsset("ds");

        GetComponent<MeshRenderer>().material.mainTexture = mat;

    }

 

 

 

    //同步与异步

    //同步:等待一件事情做完继续后面的事情,表现在代码上就是说堵塞

    //异步:不需要等待,继续做自己的事情

    void LoadMemorySync()

    {

        string path1 = Application.streamingAssetsPath + "/image.assetbundle";                  //资源包路径

        AssetBundle ab1 = AssetBundle.LoadFromMemory(File.ReadAllBytes(path1));                 //读取文件1请求

    }

 

    //同步从文件读取

    void LoadFileSync()

    {

        string path1 = Application.streamingAssetsPath + "/image.assetbundle";                                                                  

        AssetBundle ab1 = AssetBundle.LoadFromFile(path1);                   //读取文件1请求

    }

 

    //异步读取AssetBundle

    //Resource.LoadAsync

    IEnumerator LoadAsync()

    {

        string path1 = Application.streamingAssetsPath + "/image.assetbundle";

        AssetBundleCreateRequest request1 = AssetBundle.LoadFromFileAsync(path1); //读取文件1请求

        AssetBundleCreateRequest request2 = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path1)); //读取文件1请求

        //如果此处不使用协程,那么我们就通过requset.isDone进行阻塞

        yield return request1;

        yield return request2;

        //返回

        AssetBundle ab1 = request1.assetBundle;                                                       //资源1

        AssetBundle ab2 = request2.assetBundle;                                                       //资源2                    

    }

 

    IEnumerator WebServer() {

        const string url1 = @"http://127.0.0.1:8080/mat";     

        const string url2 = @"http://127.0.0.1:8080/image";

        UnityWebRequest request1 = UnityWebRequestAssetBundle.GetAssetBundle(url1);  //Unity网络请求AssetBundle.获取资源(网络地址1)

        UnityWebRequest request2 = UnityWebRequestAssetBundle.GetAssetBundle(url2);  //传入地址2

        yield return request1.SendWebRequest();                                       //发送Web请求1

        yield return request2.SendWebRequest();                                       //发送web请求2

        AssetBundle ab1 = DownloadHandlerAssetBundle.GetContent(request1);             //下载资源委托,获取连接请求1,返回AssetBundle资源

        AssetBundle ab2 = DownloadHandlerAssetBundle.GetContent(request2);              //获取连接请求2,返回AssetBundle资源

   

        //获取AsseBundle资源,可以做任意事情

    }

    IEnumerator SendRequest()

    {

        Uri uri = new Uri("http://94.191.12.62:8090/admin/allNews?page=1");

        //创建UnityWebRequest对象

        UnityWebRequest uwr = new UnityWebRequest(uri);       

        uwr.method = "GET";

        uwr.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();

 

        //创建UnityWebRequest对象,与上面的等价

      //  UnityWebRequest uwr = UnityWebRequest.Get("http://94.191.12.62:8090/admin/allNews?page=1");

        uwr.timeout = 5;

        //设置请求超时时间

        

        yield return uwr.SendWebRequest();                     //等待返回请求的信息

        if (uwr.isHttpError || uwr.isNetworkError)             //如果其 请求失败,或是 网络错误

        {

            Debug.LogError(uwr.error); //打印错误原因

        }

        else //请求成功

        {

            Debug.Log("请求成功");

            //使用DownloadHandler下载数据

            Debug.Log(uwr.downloadHandler.text);

            //解析对应的Json数据

        }

    }

 

}

1.9总结

 Unity在各平台下读取StreamingAssets文件夹中的文件是有区别的

    在移动平台下,Application.streamingAssetsPath是只读的,不能写入数据。 Application.persistentDataPath 可以读取和写入数据。

在PC下,可以用File类API(如File.ReadAllText)读写StreamingAssets文件夹中的文件;Android平台下,不能用File类API读取。

所有平台上都可以用WWW(UnityWebRequest)方式同步(异步读取StreamingAssets文件夹下AssetBundle数据,PC和IOS平台下,读取路径必须加上"file://",而安卓不需要。

iOS和Android下,还能用AssetBundle.LoadFromFile来同步异步读取AssetBundle数据不需要进行额外的路径处理

    

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值