木木的Unity学习笔记(三)—— 粗谈AssetBundle

木木的Unity学习笔记(三)—— 粗谈AssetBundle

今天听到正在学习Unity的道友说,他做了一个场景比较大的游戏,其中有大量的模型,贴图和图片等资源,导致加载时间过长,安装包也过大。他本来还想给他的朋友炫耀一下,结果朋友们纷纷都表示这游戏太烂不想玩,他很苦恼,来问我Unity有没有什么有关打包资源的东西,我就跟他简单的聊了一下AssetBundle。
那么什么是AssetBundle呢?AssetBundle是Unity提供的一种打包资源的文件格式,可以打包诸如模型、纹理、音频文件等各种资源,同时允许使用WWW类流式传输从本地或服务器等远程位置来加载资源,从而提高项目灵活性,减少初始应用程序的大小。
在Unity的Inspector面板的最下方,可以进行打包的资源都会有一栏AssetBundle,如图:

在该栏中,第一个None代表的是打包的AssetBundle的资源名称,第二个None代表的是打包的AssetBundle的后缀名,例如:

以上这样就会打包出一个materials.ab的AssetBundle文件。

创建AssetBundle文件

那么如何创建AssetBundle文件呢,在Unity5.3以上的版本中,Unity为我们提供了唯一的API来打包AssetBundle:

BuildPipeline.BuildAssetBundles(string outputPath, 
                                BuildAssetBundleOptions assetBundleOptions,
                                BuildTarget targetPlatform);

参数含义:

  1. outputPath:资源包的输出路径,资源会被编译保存到存在的文件夹里,编译的时候不会自动创建文件夹
  2. assetBundleOptions:资源包编译选项,默认为None
  3. targetPlatform:目标编译平台

我在Assets文件夹下创建了一个AssetBundles文件夹,并将一个预制体在Inspector面板上设置为prefabs,如图:


接下来创建脚本,注意脚本一定要在Editor文件夹中,如果没有就创建一个Editor文件夹,在脚本的类中加上如下的方法:

using UnityEngine;
using UnityEditor;
public class CreateAB : MonoBehaviour
{
    /// void Start() {}
    /// void Update() {}
    /// 下面是新添加的方法
    [MenuItem("Assets/Build AssetsBundle")]
    private static void ExportAssetBundle()
    {
        BuildPipeline.BuildAssetBundles("Assets/AssetBundles", 
                                        BuildAssetBundleOptions.None, 
                                        BuildTarget.Android);
    }
}

等Unity编译之后,便可以在上方菜单栏的Asset标签下找到我们定义的Build AssetBundle了,点击相当于执行这个方法:

创建完成后就会在生成目录下找到打包好的AssetBundle文件了:

加载AssetBundle文件

创建好了我们要把这个资源用起来,那么如何加载AssetBundle文件呢?Unity提供了三种不同的方法来加载AssetBundle,代码如下:
第一种:AssetBundle.LoadAsset

// 通过资源名称标识名做为参数来加载对象
public Object LoadAsset(string name);

第二种:AssetBundle.LoadAssetAsync

//执行方法类似前面的LoadAsset方法,但是在加载资源时不会阻碍主线程
public AssetBundleRequest LoadAssetAsync(string name);

第三种:AssetBundle.LoadAllAssets

// 加载AssetBundle中包含的所有资源对象,并且可以通过对象类型来过滤此资源
public Object[] LoadAllAssets(Type type);

卸载AssetBundle文件

卸载AssetBundle文件比加载要容易一些,我们需要使用AssetBundle.Unload方法来卸载AssetBundle创建出来的对象:

// 释放AssetBundle文件内存镜像,但不销毁已经加载好的Assets对象
AssetBundle.Unload(false);
// 释放AssetBundle文件内存镜像,并同时销毁所有已经加载的Assets对象
AssetBundle.Unldoad(true);

下载AssetBundle文件

下载AssetBundle文件有两种方法,分别是缓存机制和非缓存机制
第一种:不使用缓存机制(WWW类获取)
先获取WWW对象(比如命名为www),再通过www.assetBundle来加载AssetBundle对象。这样的方法其实是使用一个新建的WWW对象,AssetBundles并不会缓存到Unity在本地设备存储器上的缓存文件夹中,举例脚本:

using System;
using UnityEngine;
using System.Collections;
public class UnuseCacheLoad : MonoBehaviour
{
    // AssetBundle的下载路径(也可以是服务器的IP路径)
    public string BundleURL;
    // AssetBundle的名称
    public string AssetName;
    IEnumerator Start()
    {
        IEnumerator Start()
        {
            using (WWW www = new WWW(BundleURL))
            {
                yield return www;
                if (www.error != null)
                    throw new Exception("WWW download error by " + www.error);
                AssetBundle bundle = www.assetBundle;
                if (AssetName == string.Empty)
                    Instantiate(bundle.mainAsset);
                else
                    // 这里用到了前面所说的加载AssetBundle的方法
                    Instantiate(bundle.LoadAsset(AssetName)); 
            }
        }
    }
}

第二种:使用缓存机制(LoadFromFile方法、LoadFromMemory等方法)
通过LoadFromFile方法、LoadFromMemory等方法来下载AssetBundle文件,下载完成后会自动保存在Unity特定的的缓存区内。LoadFromFile方法从磁盘上的文件同步加载AssetBundle,该功能支持任何压缩类型的压缩包,举例脚本:

using System.IO;
using UnityEngine;
public class UseCacheLoad : MonoBehaviour
{
    private void Start()
    {
        var myLoadedAssetBundle = AssetBundle.LoadFromFile(
                                    Path.Combine(Application.streamingAssetsPath, 
                                    "AssetBundles"));
        if (myLoadedAssetBundle == null)
        {
            Debug.LogError("Failed to load AssetBundle!");
            return;
        }
        var prefab = myLoadedAssetBundle.LoadAsset<GameObject>("Cube");
        Instantiate(prefab);
        // 这里用到了前面所说的卸载AssetBundle的方法
        myLoadedAssetBundle.Unload(false);
    }
}

AssetBundle.LoadFromXXX系列方法一共有六个(每一种方法还都有两个以上的重载):

public static AssetBundle LoadFromFile(...);
public static AssetBundle LoadFromFileAsync(...);
public static AssetBundle LoadFromMemory(...);
public static AssetBundle LoadFromMemoryAsync(...);
public static AssetBundle LoadFromStream(...);
public static AssetBundle LoadFromStreanAsync(...);

一些路径相关的内容

资源文件夹的区别

资源类型 注释
Resources 是作为一个Unity3D的保留文件夹出现的,也就是如果你新建的文件夹的名字叫Resources,那么里面的内容在打包时都会被无条件的打到发布包中。它的特点简单总结一下就是:只读,即不能动态修改。所以想要动态更新的资源不要放在这里。会将文件夹内的资源打包集成到.asset文件里面。因此建议可以放一些Prefab,因为Prefab在打包时会自动过滤掉不需要的资源,有利于减小资源包的大小。主线程加载。资源读取使用Resources.Load()。
StreamingAssets 要说到StreamingAssets,其实和Resources还是蛮像的。同样作为一个只读的Unity3D的保留文件夹出现。不过两者也有很大的区别,那就是Resources文件夹中的内容在打包时会被压缩和加密。而StreamingAsset文件夹中的内容则会原封不动的打入包中,因此StreamingAssets主要用来存放一些二进制文件。下面也同样做一个简单的总结:同样,只读不可写。主要用来存放二进制文件。只能用过WWW类来读取。
AssetBundle 关于AssetBundle的介绍已经有很多了。简而言之就是把prefab或者二进制文件封装成AssetBundle文件(也是一种二进制)。但是也有硬伤,就是在移动端无法更新脚本。下面简单的总结下:是Unity3D定义的一种二进制类型。最好将prefab封装成AseetBundle,不过上面不是才说了在移动端无法更新脚本吗?那从Assetbundle中拿到的Prefab上挂的脚本是不是就无法运了?也不一定,只要这个prefab上挂的是本地脚本,就可以。使用WWW类来下载。
PersistentDataPath 看上去它只是个路径呀,可为什么要把它从路径里面单独拿出来介绍呢?因为它的确蛮特殊的,这个路径下是可读写。而且在IOS上就是应用程序的沙盒,但是在Android可以是程序的沙盒,也可以是sdcard。并且在Android打包的时候,ProjectSetting页面有一个选项Write Access,可以设置它的路径是沙盒还是sdcard。下面同样简单的总结一下:内容可读写,不过只能运行时才能写入或者读取。提前将数据存入这个路径是不可行的。无内容限制。你可以从StreamingAsset中读取二进制文件或者从AssetBundle读取文件来写入PersistentDataPath中。写下的文件,可以在电脑上查看。同样也可以清掉。

Unity中的资源路径

路径字段 注释
Application.dataPath 此属性用于返回程序的数据文件所在文件夹的路径。例如在Editor中就是Assets了。
Application.streamingAssetsPath 此属性用于返回流数据的缓存目录,返回路径为相对路径,适合设置一些外部数据文件的路径。
Application.persistentDataPath 此属性用于返回一个持久化数据存储目录的路径,可以在此路径下存储一些持久化的数据文件。
Application.temporaryCachePath 此属性用于返回一个临时数据的缓存目录。

Android中的资源路径

路径字段 具体路径
Application.dataPath /data/app/xxx.xxx.xxx.apk
Application.streamingAssetsPath jar:file:///data/app/xxx.xxx.xxx.apk/!/assets
Application.persistentDataPath /data/data/xxx.xxx.xxx/files
Application.temporaryCachePath /data/data/xxx.xxx.xxx/cache

Android中有一个小小的注意点:在Android可以是程序的沙盒,也可以是sdcard。并且在Android打包的时候,ProjectSetting页面有一个选项Write Access,可以设置它的路径是沙盒还是sdcard。
IOS中的资源路径

路径字段 具体路径
Application.dataPath Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data
Application.streamingAssetsPath Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data/Raw
Application.persistentDataPath Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Documents
Application.temporaryCachePath Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Library/Caches

猜你喜欢

转载自blog.csdn.net/fumikisama/article/details/80208364