AssetBundles 打包与几种加载方式

打包:

首先,新版本的Unity提供的打包工具 AssetBundlesBrowser 很方便,极力推荐,版本高的话可以在 Package 中找到。使用方法的话,可以参考 这篇文章 。

除此之外,手写 Editor 代码也是一个很不错的选择。代码一定要放在 Editor 文件下!主要的过程其实很简单,先通过 AssetImporter 修改一下 assetBundleName(与assetBundleVariant),然后就可以调用 BuildPipeline.BuildAssetBundles 函数进行打包操作了。

BuildPipeline.BuildAssetBundles 函数的具体用法还是参考 官网文档, 这里主要提一下第二个参数 assetBundleOptions[打包方式]  中的几个常用的选择:

None = 0 : // 对ab不进行数据压缩。输入的ab包会很大,不过输入和加载会很快

ChunkBasedCompression = 256: // 使用lz4的格式压缩ab,ab会在加载资源时才进行解压。默认的压缩格式是lzma,它会使用ab在下立即解压。

依赖关系 .manifest 文件是打包中比较让人头疼的问题,当一个资源和 与其相关联的资源 没有被打包在同一个包体中时,该资源就会对 关联资源 产生依赖关系,当我们没有加载 有依赖关系的包体时, 就会产生类似材质丢失之类的问题。这篇文章 中对依赖关系解释的很清楚。这时候,上文中提到的 AssetBundlesBrowser 就展现优势了,当你加入某一资源时,它会很清楚的提示出该资源的依赖关系,如上图中 AssetBundlesBrowser 面板上显示的,以防止出现上面提到的问题以及 重复打包造成资源浪费的问题。

下面是一个简单的Editor 打包代码:

using System;
using System.IO;
using UnityEditor;
using UnityEngine;

/// <summary>
/// 打包 AssetBundle
/// </summary>
public class AssetBundleEditor : MonoBehaviour
{
    [MenuItem("Tools/BuildAssetBundle")]
    static void BuildAssetBundle()
    {
        string outputPath = Application.dataPath + "/AssetBundles";
        if (!Directory.Exists(outputPath))
        {
            Directory.CreateDirectory(outputPath);
        }

        // 强制删除所有AssetBundle名称  
        string[] abNames = AssetDatabase.GetAllAssetBundleNames();
        for (int i = 0; i < abNames.Length; i++)
        {
            AssetDatabase.RemoveAssetBundleName(abNames[i], true);
        }

        // 删除之前的ab文件
        FileInfo[] fs = new DirectoryInfo(outputPath).GetFiles("*", SearchOption.AllDirectories);
        foreach (var f in fs)
        {
            f.Delete();
        }

        foreach (UnityEngine.Object selected in Selection.objects)
        {
            //返回所有对象相对于工程目录的存储路径如 Assets/Scenes/Main.unity, 减去 Assets 6个字节
            FindMoudles(AssetDatabase.GetAssetPath(selected).Substring(6));
        }

        BuildPipeline.BuildAssetBundles(outputPath, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget);
        AssetDatabase.Refresh();

        print("BuildAssetBundles Complete");
    }

    /// <summary>
    /// 按模块组织
    /// </summary>
    /// <param name="pathSuf"></param>
    private static void FindMoudles(string pathSuf)
    {
        string path = Application.dataPath + pathSuf;

        // 通过路径拿到文件夹
        DirectoryInfo dir = new DirectoryInfo(path);
        if (!dir.Exists) return;

        AssetImport(dir);

        // 对该文件夹的下一层文件夹进行标记
        DirectoryInfo[] subDirs = dir.GetDirectories();
        foreach (DirectoryInfo dInfo in subDirs)
        {
            string itemPath = path + "/" + dInfo.Name;
            DirectoryInfo subDir = new DirectoryInfo(itemPath);
            if (subDir.Exists)
            {
                AssetImport(subDir);
            }
        }
    }

    /// <summary>
    /// 标记某个文件夹下所有文件为文件夹名
    /// </summary>
    /// <param name="dirInfo">文件夹名</param>
    private static void AssetImport(DirectoryInfo dirInfo)
    {
        FileInfo[] files = dirInfo.GetFiles("*", SearchOption.AllDirectories);      // 取出所有文件

        foreach (FileInfo fileInfo in files)
        {
            if (fileInfo.Name.EndsWith(".mata")) continue;
            string filePath = fileInfo.FullName.Substring(fileInfo.FullName.IndexOf("Assets", StringComparison.Ordinal));       // 获取 "Assets"目录起的 文件名, 可不用转 "\\"
            AssetImporter importer = AssetImporter.GetAtPath(filePath);     // 拿到该文件的 AssetImporter
            if (importer && importer.assetBundleName != dirInfo.Name)
            {
                importer.assetBundleName = dirInfo.Name;
                importer.assetBundleVariant = "ab";
                importer.SaveAndReimport();
            }
        }
    }
}

加载资源:

资源加载的方式挺多的,除去废弃的 WWW 加载,这边提供4种加载方式:

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

/// <summary>
/// AssetBundle 资源加载
/// </summary>
public class AssetLoad : MonoBehaviour
{
    /// <summary>
    /// 资源在项目中的名字
    /// </summary>
    const string AssetName = "Cube";
    /// <summary>
    /// AB包名
    /// </summary>
    const string AssetBundleName = "prefabs.ab";
    /// <summary>
    /// AB包的 Assets 路径
    /// </summary>
    const string Path = "Assets/AssetBundles/prefabs.ab";
    /// <summary>
    /// Manifest 路径
    /// </summary>
    const string ManifestPath = "Assets/AssetBundles/AssetBundles";
    /// <summary>
    /// 服务器路径
    /// </summary>
    const string URI = @"file:///D:/Study/GitHubProjects/trunk/Might_Be_Useful/Assets/AssetBundles/prefabs.ab";
    // const string uri = @"http://localhost/AssetBundles";

    /// <summary>
    /// 1、同步加载
    /// </summary>
    public void LoadAssetsSync()
    {
        AssetBundle bundle = AssetBundle.LoadFromFile(Path);
        //AssetBundle bundle = AssetBundle.LoadFromMemory(File.ReadAllBytes(path));

        if (!bundle) return;

        UnityEngine.Object obj = bundle.LoadAsset(AssetName) as GameObject;
        //UnityEngine.Object[] objs = bundle.LoadAllAssets() as GameObject[];

        Instantiate(obj);
        bundle.Unload(false);
    }

    /// <summary>
    /// 2、异步加载
    /// </summary>
    /// <returns></returns>
    public IEnumerator LoadAssetsAsync()
    {
        // 1、LoadFromFile   获取AssetBundle
        AssetBundleCreateRequest createRequest = AssetBundle.LoadFromFileAsync(Path);
        yield return createRequest;
        AssetBundle bundle = createRequest.assetBundle;

        // 2、LoadFromMemoryAsync    获取AssetBundle
        //AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
        //yield return createRequest;
        //AssetBundle bundle = createRequest.assetBundle;       

        // LoadAssetAsync   读取资源
        AssetBundleRequest request = bundle.LoadAssetAsync(AssetName, typeof(GameObject));
        //AssetBundleRequest requests = bundle.LoadAllAssetsAsync();

        GameObject obj = request.asset as GameObject;

        Instantiate(obj);
        bundle.Unload(false);
    }
    
    /// <summary>
    /// 3、从文件夹里通过依赖关系加载
    /// </summary>
    public void LoadWithManifest()
    {
        // 加载那个打包时额外打出来的总包,不需要后缀名 .manifest
        AssetBundle manifestBundle = AssetBundle.LoadFromFile(ManifestPath);

        // AssetBundleManifest不是某个文件的名字,是固定的
        AssetBundleManifest manifest = manifestBundle.LoadAsset("AssetBundleManifest") as AssetBundleManifest;

        // 获取 AssetBundleName 资源包所依赖的资源包的名字
        string[] deps = manifest.GetAllDependencies(AssetBundleName);

        List<AssetBundle> depList = new List<AssetBundle>();    // 保存该资源所依赖的AB包

        for (int i = 0; i < deps.Length; ++i)
        {
            AssetBundle ab = AssetBundle.LoadFromFile("Assets/AssetBundles/" + deps[i]);
            
            //UnityEngine.Object obj = ab.LoadAsset(assetName);     // 具体的资源操作            
            print("AB包名: " + ab.name);

            depList.Add(ab);
        }
        
        for (int i = 0; i < depList.Count; ++i)
        {
            depList[i].Unload(false);
        }
        manifestBundle.Unload(true);
    }

    /// <summary>
    /// 4、网络加载
    /// </summary>
    /// <returns></returns>
    public IEnumerator LoadWithUnityWebRequest()
    {
        UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(URI);   // 通过 URI 获取 UnityWebRequest
        yield return request.SendWebRequest();

        //AssetBundle ab8 = ((DownloadHandlerAssetBundle)request.downloadHandler).assetBundle;
        AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);        // 通过 request 获取 AB

        // 使用里面的资源
        UnityEngine.Object obj = bundle.LoadAsset(AssetName);
        print(AssetName + " --- " + obj.name);
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值