Unity 资源打包之AssetBundle

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yu1368072332/article/details/85794608

在游戏项目开发制作过程中,开发者都需要考虑游戏中资源的的动态加载问题,为此unity提供了AssetBundle技术来满足开发者的需求。什么是AssetBundle,它是Unity引擎提供的一个存储资源的文件格式,它可以存储任意一种unity引擎能够识别的资源文件。

一、创建AssetBundle

unity提供了一个简单的AssetBundle的ui界面,可以让用户快速的将Asset标记到AssetBundle中。我们先创建一个 cube预制体:

图中我们可以看到有两个参数设置项,第一个表示资源所标记的AssetBundle名称,第二个参数表示用于设置AssetBundle  Variant,主要应用在不同版本资源的使用和动态替换AssetBundle,然后我们将测试的预制体设置成为相应的信息,然后在Editor文件中创建脚本实现资源打包:

 [MenuItem("Tools/Build AssetBundle")]
    static void CreatAssetBundle()
    {
        //先删除之前旧的资源
        string streamPath = Application.streamingAssetsPath;
        if (Directory.Exists(streamPath))
        {
            Directory.Delete(streamPath, true);
        }
        Directory.CreateDirectory(streamPath);
        //重新生成新的资源
        BuildPipeline.BuildAssetBundles(streamPath, BuildAssetBundleOptions.None, BuildTarget.Android);
    }

其中打包函数,第一个参数就是保存路径,第二个参数就是下面详细介绍,第三个参数就是目标平台 我们以安卓为例,注意各个平台之间是不会相互兼容的,安卓和ios也不能相互兼容,是要区别打包的。

第二个参数就是涉及到AssetBundle的打包方式:

        //
        // 摘要:
        //     ///
        //     Build assetBundle without any special option.
        //     ///
        None = 0,
        //
        // 摘要:
        //     ///
        //     Don't compress the data when creating the asset bundle.
        //     ///
        UncompressedAssetBundle = 1,
        //
        // 摘要:
        //     ///
        //     Includes all dependencies.
        //     ///
        CollectDependencies = 2,
        //
        // 摘要:
        //     ///
        //     Forces inclusion of the entire asset.
        //     ///
        CompleteAssets = 4,
        //
        // 摘要:
        //     ///
        //     Do not include type information within the AssetBundle.
        //     ///
        DisableWriteTypeTree = 8,
        //
        // 摘要:
        //     ///
        //     Builds an asset bundle using a hash for the id of the object stored in the asset
        //     bundle.
        //     ///
        DeterministicAssetBundle = 16,
        //
        // 摘要:
        //     ///
        //     Force rebuild the assetBundles.
        //     ///
        ForceRebuildAssetBundle = 32,
        //
        // 摘要:
        //     ///
        //     Ignore the type tree changes when doing the incremental build check.
        //     ///
        IgnoreTypeTreeChanges = 64,
        //
        // 摘要:
        //     ///
        //     Append the hash to the assetBundle name.
        //     ///
        AppendHashToAssetBundleName = 128,
        //
        // 摘要:
        //     ///
        //     Use chunk-based LZ4 compression when creating the AssetBundle.
        //     ///
        ChunkBasedCompression = 256,
        //
        // 摘要:
        //     ///
        //     Do not allow the build to succeed if any errors are reporting during it.
        //     ///
        StrictMode = 512,
        //
        // 摘要:
        //     ///
        //     Do a dry run build.
        //     ///
        DryRunBuild = 1024,
        //
        // 摘要:
        //     ///
        //     Disables Asset Bundle LoadAsset by file name.
        //     ///
        DisableLoadAssetByFileName = 4096,
        //
        // 摘要:
        //     ///
        //     Disables Asset Bundle LoadAsset by file name with extension.
        //     ///
        DisableLoadAssetByFileNameWithExtension = 8192

然后我们运行打出资源包:

这就是创建的AssetBundle文件,你会发现每个资源都会产生一个对应的mainfest文件,这个文件主要是记录当前的资源信息和依赖关系,下面我们具体看下里面内容

下面介绍其中重要的几个参数: 

  1.  CRC 对应资源文件的循环冗余校验码  可以通过这个码检查资源是否完整
  2. Assets  主要是所包含的资源
  3. Dependencies  资源依赖关系 没有就是[ ]

还有最后我们会发现还多了一个StreamingAssets文件,这是总的一个Mainfest文件,文件名和当前文件夹名字相同,然后记录当前所有的打包信息。

二、资源依赖关系

在打包中我们发现实际上我们cube和sphere是公用一个材质球的,这样打包就会在cube.unity3d和sphere.unity3d中分别打包一份这个材质球资源,造成资源浪费,现在你会发现两个文件都是153kb,那么怎么避免这个问题呢,官方提供的就是讲三个资源分别打包,他们之间就会建立对应的资源依赖关系,下面我们来测试一下:

现在你会发现,打包成三个资源,文件大小反而都减小了,现在三者总和才156kb,所以这样就可以减小文件大小,同时文件也会自动生成依赖关系,我们现在再看cube文件信息:

这时候发现在资源依赖一栏多了一个资源依赖信息,就是公用材质球的资源信息。

三、AssetBundle资源的加载和卸载:

打包完成之后,我们说下如何加载AssetBundle,并从中取出Asset资源,同时卸载加载完成的AssetBundle信息。

我们这里以从本地加载为例,和从服务器加载方式是一样的 ,我们加载出其中的cube为例进行讲解:

    //变量定义    
    public const string mainfestName = "StreamingAssets";  //总的Mainfest文件名字
    private string assetBundleName; //要加载的资源名称
    private const string suffixName = ".unity3d";  //后缀名
    private string url;  //资源加载路径

    void Start()
    {
        assetBundleName = "cube";  //添加要加载的资源名
        url = "file://" + Application.streamingAssetsPath + "/";   //路径赋值
    }

再具体加载过程中,如果AssetBundle之间存在依赖关系,就要先加载总的Manifest文件,然后再加载AssetBundle。

从上图看出,如果需要加载cube,就需要先加载common.unity3d资源信息,具体代码如下:

        
        //加载Manifest资源文件
        WWW www = WWW.LoadFromCacheOrDownload(url + mainfestName, 0);
        yield return www;
        if (www.error != null)
        {
            Debug.LogError("资源下载失败:" + www.error);
            yield break;
        }
        //获取总的Manifest资源信息
        AssetBundle mainfestBundle = www.assetBundle;
        AssetBundleManifest mainfest = (AssetBundleManifest)mainfestBundle.LoadAsset("AssetBundleManifest");
        mainfestBundle.Unload(false);

获取当前资源的依赖关系,例如cube就会获取到common的依赖的关系:

        //获取资源依赖列表
        string[] dependentAssetBundles = mainfest.GetAllDependencies(assetBundleName + suffixName);
        AssetBundle[] abs = new AssetBundle[dependentAssetBundles.Length];
        for (int i = 0; i < dependentAssetBundles.Length; i++)
        {
            //加载所有依赖关系
            WWW www_re = WWW.LoadFromCacheOrDownload(url + dependentAssetBundles[i], 0);
            yield return www_re;
            abs[i] = www_re.assetBundle;
        }

然后加载cube资源信息:

        WWW www1 = WWW.LoadFromCacheOrDownload(url + assetBundleName + suffixName, 0);
        yield return www1;
        AssetBundle assetBundle = www1.assetBundle;

        //从AssetBundle中加载Asset资源
        AssetBundleRequest request = assetBundle.LoadAssetAsync(assetBundleName, typeof(GameObject));
        yield return request;
        GameObject go = request.asset as GameObject;
        GameObject cube = Instantiate(go) as GameObject;
        cube.transform.localPosition = Vector2.zero;
        ResetShader(cube);
        //卸载资源
        assetBundle.Unload(false);

现在就是所有的资源加载以及卸载流程,下面是全部代码信息:

    IEnumerator LoadResource()
    {
        WWW www = WWW.LoadFromCacheOrDownload(url + mainfestName, 0);
        yield return www;
        if (www.error != null)
        {
            Debug.LogError("资源下载失败:" + www.error);
            yield break;
        }
        //获取总的Manifest资源信息
        AssetBundle mainfestBundle = www.assetBundle;
        AssetBundleManifest mainfest = (AssetBundleManifest)mainfestBundle.LoadAsset("AssetBundleManifest");
        mainfestBundle.Unload(false);
        //获取资源依赖列表
        string[] dependentAssetBundles = mainfest.GetAllDependencies(assetBundleName + suffixName);
        AssetBundle[] abs = new AssetBundle[dependentAssetBundles.Length];
        for (int i = 0; i < dependentAssetBundles.Length; i++)
        {
            //加载所有依赖关系
            WWW www_re = WWW.LoadFromCacheOrDownload(url + dependentAssetBundles[i], 0);
            yield return www_re;
            abs[i] = www_re.assetBundle;
        }

        WWW www1 = WWW.LoadFromCacheOrDownload(url + assetBundleName + suffixName, 0);
        yield return www1;
        AssetBundle assetBundle = www1.assetBundle;

        //从AssetBundle中加载Asset资源
        AssetBundleRequest request = assetBundle.LoadAssetAsync(assetBundleName, typeof(GameObject));
        yield return request;
        GameObject go = request.asset as GameObject;
        GameObject cube = Instantiate(go) as GameObject;
        cube.transform.localPosition = Vector2.zero;
        ResetShader(cube);
        //卸载资源
        assetBundle.Unload(false);
    }

实现效果:

四、关于部分shader丢失问题的解决

 在实际项目中,可能有时候会发现加载的资源是粉色的,也就是材质丢失了,如下图:

 针对这种情况,就是需要在加载资源之后,对shader重新复制刷新一下就OK了,具体分为两步:

1.需要将对应的shader加载到资源中去,具体就是讲用到的shader放到设置中去,具体操作就是将用到的Shader加Edit->Graphics Settings的Shader列表里再进行打包即可:

2.就是对当前的材质重新赋值:

    /// <summary>
    /// 重置 shader信息
    /// </summary>
    /// <param name="obj"></param>
    public static void ResetShader(UnityEngine.Object obj)
    {
        List<Material> listMat = new List<Material>();
        if (obj is Material)
        {
            Material m = obj as Material;
            listMat.Add(m);
        }
        else if (obj is GameObject)
        {

            GameObject go = obj as GameObject;
            Renderer[] rends = go.GetComponentsInChildren<Renderer>(true);
            if (null != rends)
            {

                foreach (Renderer item in rends)
                {
                    Material[] materialsArr = item.sharedMaterials;
                    foreach (Material m in materialsArr)
                        listMat.Add(m);
                }
            }
        }
        else if (obj is Transform)
        {
            GameObject go = (obj as Transform).gameObject;
            Renderer[] rends = go.GetComponentsInChildren<Renderer>(true);
            if (null != rends)
            {
                foreach (Renderer item in rends)
                {
                    Material[] materialsArr = item.sharedMaterials;
                    foreach (Material m in materialsArr)
                        listMat.Add(m);
                }
            }
        }
        else if (obj is RectTransform)
        {
            GameObject go = (obj as RectTransform).gameObject;
            Renderer[] rends = go.GetComponentsInChildren<Renderer>(true);
            if (null != rends)
            {
                foreach (Renderer item in rends)
                {
                    Material[] materialsArr = item.sharedMaterials;
                    foreach (Material m in materialsArr)
                        listMat.Add(m);
                }
            }
        }
        for (int i = 0; i < listMat.Count; i++)
        {
            Material m = listMat[i];

            if (null == m)
                continue;
            var shaderName = m.shader.name;
            var newShader = Shader.Find(shaderName);
            if (newShader != null)
            {
                m.shader = newShader;
            }
        }
    }

 通过上述两步操作,就可以恢复加载资源对应的shader信息。

工程链接:https://pan.baidu.com/s/1K2YVAL03bozg_5HfPqqA2Q     提取码:frc9 


如果想学习更多unity相关知识可以关注下方公众号哦:

猜你喜欢

转载自blog.csdn.net/yu1368072332/article/details/85794608