(一)ab是什么,打包流程简述
ab是一种资源的加载方式,是一种压缩文件的格式
为什么需要ab?
如果一个游戏的所有资源都打包成一整个资源,那游戏包会很大,如果是每个关卡的资源,每个关卡的加载,包会小点,所以ab就是把各种类型的资源如音乐,图片,场景,音效等变成一个ab包放到服务器上,然后客户端联网后,从服务器上下载,在本地动态加载资源,以达到资源更新的目的
ps:更新有资源热更和代码热更,代码热更需要通过c#+xlua框架的方式
(二)打包步骤
1.设置打包对象
2.生成ab包
3.上传到服务器
4.加载ab包中的资源
5.使用ab包中的资源
(三)步骤一:设置打包对象

这里的assetbundle是打包的包名,后面的那个none是扩展名
即可以给这个方块资源设置成包名为fangkuai,扩展名为u3d
(四)用代码生成ab包
1.assets下新建一个Editor文件夹,因为打包是在编辑下进行的。
2.在这个Editor文件夹下建立一个脚本,脚本在编辑状态下,所以不继承monobehaviour,继承Editor,且引入命名空间using UnityEditor;
注意:不要运行unity点击按钮打包,直接等编译完,点击按钮就可以了。

using UnityEditor;
using System.IO;//创建文件夹要用的包

public class MyTools
{
    [MenuItem("Tools/CreateAssetBundles")]//在uinty上建一个菜单Tools下有个CreateBundles的菜单

    static void CreateAssetBundles()//这是点击这个按钮的功能
    {
        
        //打包的路径
        string assetBundleDirectory = "StreamingAssets";
        //如果没有就在工程下创建一个这样的路径
        if (!Directory.Exists(assetBundleDirectory))
        {
            Directory.CreateDirectory(assetBundleDirectory);
        }
        //打包资源:参数解析:assetBundleDirectory:打包到哪里,目录
        //BuildAssetBundleOptions:none默认的打包方式
        //BuildTarget:打包的平台
        BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneOSX);

        UnityEngine.Debug.Log("打包完成");
    }

    
   
}

编译完打包出来的目录是这样的

点开streamingAssets是这样的资源清单和一些依赖关系

如何打包归类:
1.每种类型打个包
2.或者一个界面,关卡的资源打个包。

打包要注意的:
如果不把模型用的mat.u3d打包,而是把这三个预置体打包,那么这三个预置体中的材质都会被打包一次,每个预制体大概是20多kb,如果是把mat.u3d打包了,每个预制体的材质也相当于打包了,每个预制体是几kb。

(五)读取assetbundle的四种方式(内存,文件,www,unitywebrequest)
建立一个脚本,将脚本绑定到空对象上
注意:加载都用协程
(1)从内存中加载ab包中的单个资源

public class LoadAssetBundle : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(LoadFromMemory());
    }

    //从内存读取ab包
    IEnumerator LoadFromMemory()
    {
        //要找的ab包里的资源的路径
        string boxPath = @"Desktop/LittleSomethingsTest/StreamingAssets/box.u3d";
        string matPath = @"Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
        //把资源读取成byte[]
        byte[] boxBytes = File.ReadAllBytes(boxPath);
        byte[] matBytes= File.ReadAllBytes(matPath);
        //加载ab包
        AssetBundle boxAB = AssetBundle.LoadFromMemory(boxBytes);
        AssetBundle matAB = AssetBundle.LoadFromMemory(matBytes);
        //取出ab包里的资源,名字不是打包的名字,是从捆绑包中加载名为 name 的资源,原始的名字
        GameObject boxPrefab = boxAB.LoadAsset<GameObject>("Cube");
        //实例化资源
        Instantiate(boxPrefab);
        yield return null;

    }
}

注意:这里因为事材质也分别打了包,所以加载ab包时,不能光加载模型,而是要把模型的材质也加载进来,不然模型丢失材质会变粉。
(2)从文件中加载ab包中的单个资源

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class LoadAssetBundle : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(LoadFromFile());
    }

    //从单个文件中读取ab包
    IEnumerator LoadFromFile()
    {
        //要找的ab包里的资源的路径,材质也要加载
        string boxPath = @"Desktop/LittleSomethingsTest/StreamingAssets/box.u3d";
        string matPath = @"Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
        //加载资源包和材质包
        AssetBundle boxAB = AssetBundle.LoadFromFile(boxPath);
        AssetBundle matAB = AssetBundle.LoadFromFile(matPath);
        //取出ab包的资源实例化
        GameObject boxPrefab = boxAB.LoadAsset<GameObject>("cube");
        Instantiate(boxPrefab);
        yield return null;
    }
}

(3)将多个资源打入一个ab包,然后从文件读取ab包中的多个资源

1.把要打包的模型设置成同一个包名allobjects,扩展名都是u3d,材质单独一个包名mat
如图所示

2.代码实现

//从文件中读取ab包中的多个资源对象
    IEnumerator AllFromFile()
    {
        //要加载的ab包路径
        string prefabsPath = @"Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
        string matPath = @"Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
        //从文件加载ab包
        AssetBundle prefabsAB = AssetBundle.LoadFromFile(prefabsPath);
        AssetBundle matAB = AssetBundle.LoadFromFile(matPath);
        //从ab包中加载所有资源
        Object[] objList = prefabsAB.LoadAllAssets();
        //遍历这些资源,然后实例化
        foreach(var go in objList)
        {
            Debug.Log(((GameObject)go).name);
            Instantiate(((GameObject)go));
        }
        yield return null;
    }

(4)www方式无缓存机制的加载ab包的方式
www有两种方式,本地和http的,下面是本地方式

//用www的方式无缓存机制本地加载ab包中的资源
    IEnumerator FromWWW1()
    {
        //资源的www
        string prefabsPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
        WWW prefabsWWW = new WWW(prefabsPath);
        yield return prefabsWWW;//等www加载完,似乎这句不加也不影响加载?
        if (!string.IsNullOrEmpty(prefabsWWW.error))//如果加载有错就输出错误
        {
            Debug.Log("加载错误" + prefabsWWW.error);
        }
        //使用加载出来的ab包
        AssetBundle prefabsAB = prefabsWWW.assetBundle;
        Object[] objList = prefabsAB.LoadAllAssets();
        foreach(var go in objList)
        {
            Instantiate(((GameObject)go));
        }

        //材质的www
        string matPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
        WWW matWWW = new WWW(matPath);
        //yield return matWWW;//注意如果这里加了这句,加载场景的时候prefab会先变粉再变成有材质的颜色,这是为什么呢……
        AssetBundle matAB = matWWW.assetBundle;//这里别忘了材质的ab包
        yield return null;
    }

(5)带有缓存机制的www

 //有缓存机制的www方式加载ab包:即当缓存有时用缓存里的ab包,缓存没有时,再去地址下载ab包
    IEnumerator FromWWWHC()
    {
        string prefabsPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
        //参数解析:第一个是下载ab包的路径,第二个是哈希数值用来识别版本号,当要求加载的路径和版本号都对上时,从缓存中加载,即版本号用来识别是缓存里的ab包的哪一个下载了的版本。
        WWW prefabsWWW = WWW.LoadFromCacheOrDownload(prefabsPath, 1);
        if (!string.IsNullOrEmpty(prefabsWWW.error))//如果加载有错就输出错误
        {
            Debug.Log("加载错误" + prefabsWWW.error);
            yield break;
        }
        AssetBundle prefabsAB = prefabsWWW.assetBundle;
        Object[] objList = prefabsAB.LoadAllAssets();
        foreach (var go in objList)
        {
            Instantiate(((GameObject)go));
        }

        //材质的ab包
        string matPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
        WWW matWWW = WWW.LoadFromCacheOrDownload(matPath, 1);
        AssetBundle matAB = matWWW.assetBundle;

        yield return null;
    }

ps:www对象还可以释放,通过添加
prefabsWWW.Dispose();
prefabsWWW = null;
(6)用unityWebRequest方式(新版本)
引入命名空间:using UnityEngine.Networking;

//用uintyWebRequest加载ab包
    IEnumerator FromUnityWebRequest()
    {
        string prefabsPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
        UnityWebRequest prefabsRequest;
        prefabsRequest = UnityWebRequestAssetBundle.GetAssetBundle(prefabsPath);//命令从这个路径获取ab包
        yield return prefabsRequest.SendWebRequest();//命令之后,还要发送这个命令
        //api注释调用此方法后,UnityWebRequest 将执行 DNS 解析(如有必要),将一个 HTTP 请求传输到位于目标 URL 的远程服务器,并处理该服务器的响应。
        //在任何给定的 UnityWebRequest 对象上,此方法只能调用一次。
        AssetBundle prefabsAB = DownloadHandlerAssetBundle.GetContent(prefabsRequest);//从unitywebrequest中的对象获取内容
        Object[] objList = prefabsAB.LoadAllAssets();
        foreach (var go in objList)
        {
            Instantiate(((GameObject)go));
        }

        //读取资源
        string matPath = @"file:///Desktop/LittleSomethingsTest/StreamingAssets/mat.u3d";
        UnityWebRequest matRequest;
        matRequest = UnityWebRequestAssetBundle.GetAssetBundle(matPath);
        yield return matRequest.SendWebRequest();//如果把这个注释掉,这个matrequest就是个未完成的请求对象,后面材质获取不到
        AssetBundle matAB = DownloadHandlerAssetBundle.GetContent(matRequest);
        yield return null;
    }

ps:这种写法会预制体先试粉的,然后有材质,所以,可以让材质在预制体之前加载就可以了。写的顺序换一下。

(7)从文件通过主manifest文件加载所有的ab包

//以文件方式为例,通过主manifest文件加载所有资源和所有依赖项
    IEnumerator LoadAssetsFromManifest()
    {
        string manifestPath = @"Desktop/LittleSomethingsTest/StreamingAssets/StreamingAssets";
        AssetBundle manifestAB = AssetBundle.LoadFromFile(manifestPath);
        //注意这里manifest的名字不是文件夹中的StreamingAssets名字,而是固定的AssetBundleManifest
        AssetBundleManifest manifest = manifestAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        // manifest.GetAllAssetBundles()这个获得的是个string[],是所有ab包的名字及其扩展名的一个字符串数组
        foreach(string str in manifest.GetAllAssetBundles())
        {
            string abPath = @"Desktop/LittleSomethingsTest/StreamingAssets/" + str;
            AssetBundle ab = AssetBundle.LoadFromFile(abPath);//每一个ab包中可能有多个资源,遍历然后实例化
            object[] objList = ab.LoadAllAssets();
            foreach(var go in objList)//因为这里是所有的ab包,其中也包含了不是游戏物体的材质,所以这里要判断一下,是材质才创建实例
            {
                //因为这里是所有的ab包,其中也包含了不是游戏物体的材质,所以这里要判断一下,是材质才创建实例
                //不然转换会报错
                if (go is GameObject)
                {
                    Instantiate(((GameObject)go));
                }
                
            }

            
        }

        yield return null;
    }

(8)从文件,通过主manifest加载特定ab包的依赖项

//以文件方式为例,通过主manifest文件获取特定ab包的依赖
    IEnumerator LoadDependenciesFromManifest()
    {
        string prefabsPath = @"Desktop/LittleSomethingsTest/StreamingAssets/allobjects.u3d";
        AssetBundle prefabsAB = AssetBundle.LoadFromFile(prefabsPath);
        Object[] objList = prefabsAB.LoadAllAssets();
        foreach(var go in objList)
        {
            Debug.Log(((GameObject)go).name);
            Instantiate(((GameObject)go));
        }
        //上面这几行代码是加载了ab包中的预制体,但是因为没有加载其依赖项,所以都是粉色的
        //下面通过主manifest添加资源的依赖项
        string manifestPath = @"Desktop/LittleSomethingsTest/StreamingAssets/StreamingAssets";
        AssetBundle manifestAB = AssetBundle.LoadFromFile(manifestPath);
        AssetBundleManifest manifest = manifestAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        string[] strs = manifest.GetAllDependencies("allobjects.u3d");//得到这个包的所有依赖项的名字的字符串数组
        //加载依赖项的ab包
        foreach(string s in strs)
        {
            AssetBundle.LoadFromFile(@"/Users/Magician/Desktop/LittleSomethingsTest/StreamingAssets/"+s);
        }


        yield return null;
    }

(9)服务器上加载ab包同理,改路径为地址就行

本文地址:https://blog.csdn.net/qq_42385019/article/details/107320383