Unity-AB包

什么是AB包:它是一个存在于硬盘上的文件,可以称为压缩包,这个压缩包可以是一个文件夹。

AB包的作用:将资源放到服务器上,当客户端需要的时候从服务器中获取,程序不需要重新安装。

AB包定义:Unity资源压缩包。
AB包依赖的资源:图片,文本,音频,视频,模型,动画,预制体,场景资源,脚本,可编辑脚本资源。

图片支持的格式:.png,.jpg。
文本支持的格式:.txt,.json,.xml。
音频支持的格式:.mp3,.wav,.ogg。
视频支持的格式:.avi,.mp4。
模型支持的格式:.fbx,.obj。
动画支持的格式:.animationClip(fbx内部动画片段)。
预制体支持的格式:配置文件(包含当前游戏物体所依赖的所有组件信息)。
场景资源支持的格式:配置文件(包含当前场景中所有物体的配置信息)。
脚本支持的格式:C#脚本。
可编辑脚本资源:.asset文件。

压缩包可以使用LZMA和LZ4压缩算法,较少包大小,更快的进行网络传输。
压缩后的资源不能直接使用,需要进行解压。

把一些要下载的内容放到AB包中可以减少安装包的大小。

资源冗余:多个包引用同一个资源但引用的资源没有打入AB包。

颗粒度:颗粒度太大会导致不太容易替换资源,过度消耗存储性能,颗粒度太小会导致降低IO读写速度,影响用户体验。

打包策略
1:可以将资源分的很细进行打包,基于每个资源都打一个AB包。
2:一个模块打一个AB包,可以基于文件夹进行打包。

打出来的文件

AB文件

CRC 资源校验
AssetBundleInfos 当前打出的所有的资源包

其他AB配置文件(一个命名一个AB包)

CRC 资源包校验码,资源包的唯一ID。
Hash 每次生成的资源包都会生成一个哈希值,用于做资源包的对比。
Asset 打入当前AB包中的所有的资源。

注:

1:AB包的路径是小写。
2:不标记AssetBundle是不会打到AB包中,标记相同的AssetBundle将会打到一个AB包中。
3:多个AB包都依赖的资源一定要进行打包。

模型A和模型B依赖材质C,模型A和模型B不在同一个AB包内
模型A和模型B进行打包,材质C没有进行打包,会导致模型A的AB包内出现材质C,模型B的AB包内出现材质C,下载的资源将会增大。
模型A和模型B进行打包,材质C单独进行打包,模型A的AB包和模型B的AB包对材质C的AB包产生依赖关系,加载模型A的时候是要加载模型A所在的资源包和材质C所在的资源包,不会导致下载资源增加。
模型A和模型B进行打包,材质C和模型A打到了一个AB包内,模型B所在的AB包会和材质C所在的AB包产生依赖关系,加载模型B的时候是需要加载模型B所有的资源包和材质C所在的资源包,不会导致下载资源增加。
4:要打包的资源是需要设置后缀名的。

具体操作

1:服务器·给资源批量设置名称并打成AB包(要放到Editor文件夹下)

// 批量打包工具
using System.IO;
using UnityEditor;
using UnityEngine;

public class ABCreator : EditorWindow {
    [MenuItem("Plugins/资源包打包工具")]
    static void Init() {
        ABCreator abt = EditorWindow.GetWindow<ABCreator>();
        abt.Show();
    }

    // 输入路径
    string inputPath;
    // AB包输出路径
    string outPutPath;
    // 选择的平台类型
    BuildTarget selectedTarget;


    public void OnGUI() {

        // 选择打包输出路径
        if (GUILayout.Button("选择输入路径")) {
            inputPath = EditorUtility.OpenFolderPanel("选择输入路径", Application.dataPath, "");
        }

        // 选择打包输出路径
        if (GUILayout.Button("选择输出路径")) {
            outPutPath = EditorUtility.OpenFolderPanel("选择输出的路径", Application.dataPath, "");
        }

        // 选择对应的打包平台
        selectedTarget = (BuildTarget)EditorGUILayout.EnumPopup(selectedTarget);

        // 设置要打包的AB包名称
        if (GUILayout.Button("批量设置名称")) {
            SetABNames(inputPath);
        }

        // 资源包打包按钮
        if (GUILayout.Button("资源包打包")) {
            CreateAB();
        }
    }

    private void SetABNames(string input) {
        // 获得当前路径下的所有文件夹
        string[] paths = Directory.GetDirectories(input);
        foreach (var item in paths) {
            SetABNames(item);
            // 获得当前路径下的文件
            string[] filePaths = Directory.GetFiles(item);
            foreach (var data in filePaths) {
                if (data.Contains(".meta"))
                    continue;
                // 将路径中的右斜杠替换成左斜杠
                var temp = data.Replace(@"\", "/");
                // 将Asset文件的绝对路径换成Assets
                temp = temp.Replace(Application.dataPath, "Assets");
                AssetImporter asset = AssetImporter.GetAtPath(temp);
                // ab包的名称为当前资源的路径,但不包含文件名
                var abName = temp.Substring(0, temp.LastIndexOf("/"));
                asset.assetBundleName = abName + ".ab";
            }
        }
    }

    private void CreateAB() {
        BuildPipeline.BuildAssetBundles(outPutPath, BuildAssetBundleOptions.None, selectedTarget);
    }
}

2:服务器·将打好的AB包放到资源服务器(资源服务和游戏服务器不是同一个服务器)

1:客户端·获取服务器清单文件,(打包自动生成在根目录和根目录同名的文件)清单文件就是每个AB包下载的路径

2:客户端·下载AB包(打包自动生成在根目录和根目录同名的文件),通过短连接,Http get请求

3:客户端·从AB包中解压出我想要的资源文件,清单文件

4:客户端·得到当前所有的资源AB包的下载路径,得到每个资源AB包中的哈希值

5:客户端·对比本机已经下载好的资源和本次要下载的AB包资源的哈希值

6:客户端·下载AB包,通过短连接,Http get请求

// 客户端下载测试
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ResourceMgr : MonoBehaviour
{

    public string 清单网址;
    public string 清单资源文件名;

    [ContextMenu("下载")]
    public void 下载清单资源包()
    {
        StartCoroutine(开启协程下载清单资源包());
    }

    private IEnumerator 开启协程下载清单资源包()
    {
        WWW www = new WWW(清单网址 + "/" + 清单资源文件名);
        yield return www;
        if (www.isDone && www.error == null)
        {
            AssetBundle 清单文件资源包 = www.assetBundle;
            AssetBundleManifest 资源包清单文件数据 = 清单文件资源包.LoadAsset<AssetBundleManifest>("assetbundlemanifest");
            foreach (var 下载路径 in 资源包清单文件数据.GetAllAssetBundles())
            {
                Hash128 哈希值 = 资源包清单文件数据.GetAssetBundleHash(下载路径);
                yield return StartCoroutine(开始下载指定的资源包(下载路径, 哈希值));
            }
        }
    }

    static Dictionary<string, AssetBundle> 资源缓存 = new Dictionary<string, AssetBundle>();

    private IEnumerator 开始下载指定的资源包(string 下载的路径,Hash128 比对哈希值)
    {
        WWW www = WWW.LoadFromCacheOrDownload(清单网址 + "/"  +下载的路径, 比对哈希值);
        yield return www;
        if (www.isDone && www.error == null)
        {
            AssetBundle 下载的资源AB包 = www.assetBundle;
            Debug.LogError("当前获取的资源包名" + 下载的资源AB包.name);
            foreach(var 资源路径 in 下载的资源AB包.GetAllAssetNames())
            {
                资源缓存.Add(资源路径, 下载的资源AB包);
            }
        }
    }

    public static T Load<T>(string path) where T:UnityEngine.Object
    {
        Debug.Log("加载资源的路径" + path);
        if (资源缓存.ContainsKey(path))
        {
           return 资源缓存[path].LoadAsset<T>(path);
        }
        return null;
    }
}
posted @ 2022-09-25 18:39  坞中客  阅读(89)  评论(0编辑  收藏  举报