zoukankan      html  css  js  c++  java
  • Unity3D学习笔记(二十九):AssetBundle

    AssetBundle
    什么是AssetBundle?
    AssetBundle是把一些资源文件或场景文件,以某种方式保存在一个文件中。一个AssetBundle可以包含模型、材质、图片或场景等。但是AssetBundle不能包含脚本文件。(脚本打包时需要编译,界面逻辑的热更新依赖Lua)
    AssetBundle主要用于做热更新使用。
     
    如何创建AB包
    第一个参数:
    ----无
    ----新建
    ----删除未使用的AssetBundle
    第二个参数:
    ----后缀:做资源高清和标清的区分,不能做资源AB包的区分
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    public class CreateAssetBundle : MonoBehaviour {
        [MenuItem("AssetBundle/CreateAB")]
        static void CreateAB()
        {
            //第一个参数:AB包的输出路径
            //第二个参数:打包的参数设置,我们设置的是强制性的重新打包
            //第三个参数:AB包的适用平台,不同的平台使用的AB包是不一样的
            BuildPipeline.BuildAssetBundles(
                Application.streamingAssetsPath + "/AssetBundle/",
                BuildAssetBundleOptions.ForceRebuildAssetBundle,
                BuildTarget.StandaloneWindows64
                );
        }
    }
     
    .meta文件:记录该资源在Unity里的相关设置(配置信息表)
    作用:SVN团队合作时,同时更新资源和.meta文件
     
    如何使用AB包里的文件
    注意:避免在一个AB包里出现同名同类型文件,只会加载其中一个
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    public class LoadABTexture : MonoBehaviour {
        private Image image;
        private void Awake()
        {
            image = GetComponent<Image>();
        }
        // Use this for initialization
        void Start () {
            //要是AB包中的资源文件
            //第一种方式:先加载AB包
            //AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/ui");
            //第二种方式:
            WWW www = new WWW("file://" + Application.streamingAssetsPath + "/AssetBundle/ui");
            AssetBundle ab = www.assetBundle;
            Sprite sp = ab.LoadAsset<Sprite>("beijing_02.jpg");
            image.sprite = sp;
            image.SetNativeSize();
        }
    }
     
    打包加载Json.txt文件
    AssetBundle jsonAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/json");
    TextAsset json = jsonAB.LoadAsset<TextAsset>("Json");
    Debug.Log(json);
     
    打包加载预制体
    AB包的依赖性
    如果一个AB包a中的资源,使用了另一个AB包b中的资源,那么这个a就依赖于b。
    当你使用AB包a中的资源的时候,需要先把依赖的AB包b先加载进来。
    三层依赖关系(常用):预制体prefab->材质mat->图片ui
    四层依赖关系(难做)
    注意:同一个AB包(在卸载之前)只能加载一次(不管是否在同一脚本中,只要是在一个项目中)
    //从AB包中加载预制体,把预制体实例到界面
    //加载AB包
    AssetBundle uiAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/ui");
    AssetBundle matAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/mat");
    AssetBundle prefabAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/prefab");
    //从AB包中加载预制体
    GameObject prefab = prefabAB.LoadAsset<GameObject>("Cube");
    //实例化预制体
    Instantiate(prefab, transform);

    每一个AB包都对应一个.manifest文件(资源清单),包含如下信息

    ManifestFileVersion: 0//版本号
    CRC: 1860656504//CRC循环冗余码
    Hashes://资源文件的哈希码,用于检查增量的构建AB包
      AssetFileHash:
        serializedVersion: 2
        Hash: 82edae0095f36a303261e62246d831ae
      TypeTreeHash:
        serializedVersion: 2
        Hash: 81fd706e1561f1cfc1872b1168421ee0
    HashAppended: 0
    ClassTypes://该AB包中所有资源使用到的类类型,一般情况下对应的都是地址
    - Class: 1
      Script: {instanceID: 0}
    - Class: 114
      Script: {fileID: 11500000, guid: d533bd1959b71b4459e871de8a9975af, type: 3}
    - Class: 115
      Script: {instanceID: 0}
    Assets://对应该AB包中的所有资源
    - Assets/Prefabs/Cube.prefab
    Dependencies://该AB包的直接依赖
    - "D:/Unity3Du6E38u620Fu5F00u53D1u5DE5u7A0Bu5E08u73ED1803u671F-u706Bu661Fu65F6u4EE3/Unity3D/Unity_Projects/m3w3d2_Lesson32/Assets/StreamingAssets/AssetBundle/ui"//路径里的中文用16进制显示
    单一的AssetBundle.Manifest文件:
    1、所有的AssetBundle
    2、所有的AB包的依赖(可以通过获取AssetBundle.Manifest文件,来获取AB包的依赖关系)
    注意:AssetBundle.Manifest文件的名字,会根据父文件夹的名字改变
    ManifestFileVersion: 0
    CRC: 1678997246
    AssetBundleManifest:
      AssetBundleInfos:
        Info_0:
          Name: ui
          Dependencies: {}
        Info_1:
          Name: mat
          Dependencies: {}
        Info_2:
          Name: prefab
          Dependencies:
            Dependency_0: ui
     
    不知道资源的依赖关系,自动去获取依赖项和加载依赖项
    使用AB包的步骤
    1、加载总的构建的AB包
    2、从单一的AB包中去加载构建清单
    3、从构建清单中获取指定的AB包的所有的依赖项,并加载所有的依赖项
    4、加载资源所在的AB包
    5、从AB包中加载资源
    //使用AB包的步骤
    //1、加载总的构建的AB包
    AssetBundle singleAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/AssetBundle");
    //2、从单一的AB包中去加载构建清单
    AssetBundleManifest singleManifest = singleAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
    //3、从构建清单中获取指定的AB包的所有的依赖项,并加载所有的依赖项
    //singleManifest.GetAllDependencies("prefab");获取所有的依赖,不管直接还是间接
    //singleManifest.GetDirectDependencies("prefab");获取所有的直接依赖
    string[] deps = singleManifest.GetAllDependencies("prefab");
    for (int i = 0; i < deps.Length; i++)
    {
        //Debug.Log(deps[i]);
        AssetBundle depAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/" + deps[i]);
    }
    //4、加载资源所在的AB包
    AssetBundle prefabAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/prefab");
    //5、从AB包中加载资源
    GameObject prefab = prefabAB.LoadAsset<GameObject>("Cube");
    Instantiate<GameObject>(prefab, transform);
    public string[] GetAllDependencies(string assetBundleName);//获取所有依赖(老版本没有)
    public string[] GetDirectDependencies(string assetBundleName);//获取直接依赖
    AB包的卸载
    Unload(bool)
    当传入true时,不光卸载内存中的AssetBundle对象,还卸载从这个AB包中加载到的资源。
    当传入false时,只卸载内存中的AB包,不卸载从AB中加载的资源。
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class UnloadAB : MonoBehaviour {
        public AssetBundle uiAB;
        public AssetBundle matAB;
        public AssetBundle prefabAB;
        public GameObject prefab;
        // Use this for initialization
        void Start () {
            uiAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/ui");
            matAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/mat");
            prefabAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/AssetBundle/prefab");
            prefab = prefabAB.LoadAsset<GameObject>("Cube");
            Instantiate<GameObject>(prefab, transform);
        }
           
           // Update is called once per frame
           void Update () {
            if (Input.GetKeyDown(KeyCode.Space))//按空格键卸载
            {
                uiAB.Unload(false);
                matAB.Unload(false);
                prefabAB.Unload(false);
            }
        }
    }
    字典:
    简介:
    1、字典里的每一个元素都是一对键值对(由两个元素组成:一个是键一个是值)
    2、字典的键必须是唯一的,值不需要唯一
    3、键和值都可以是任意的类型(比如:基本类型,自定义类型)
    4、可以通过键去访问值
     
    字典的使用:
    1、必须引用命名空间using System.Collections.Generic;
    2、
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class LessonDictionary : MonoBehaviour {
    
    
        //动态数组的申请方式
        List<string> list = new List<string>();
        //定义一个键是string类型,值是string类型的字典
        Dictionary<string, string> dic1 = new Dictionary<string, string>();
        //定义一个键是string类型,值是GameObject类型的字典
        Dictionary<string, GameObject> dic2 = new Dictionary<string, GameObject>();
    
    
        // Use this for initialization
        void Start () {
            //对list进行添加元素
            list.Add("nihao");
            //字典的添加元素,添加的是键值对,第一个参数键,第二个参数值
            dic1.Add("a", "A");
            //这种方式也能添加元素
            dic1["b"] = "C";
    
    
            //list改变值的方式
            list[0] = "1";
            //字典改变这个键对应的值的方式
            dic1["a"] = "B";
    
    
            //list通过索引访问元素
            Debug.Log(list[0]);
            //字典通过键去访问这个键对应的值
            Debug.Log(dic1["a"]);
            Debug.Log(dic1["b"]);
    
    
            //list删除元素
            list.Remove("1");
            //字典删除元素,通过键去删除这个键值对
            dic1.Remove("a");
    
    
            //list的遍历
            for (int i = 0; i < list.Count; i++)
            {
                Debug.Log(list[i]);
            }
            foreach (var item in list)//不能改值,且效率低,产生内存碎片
            {
                Debug.Log(item);
            }
    
    
            //字典的遍历
            //遍历字典的键
            foreach (var item in dic1.Keys)
            {
                Debug.Log(item);//打印的键
                Debug.Log(dic1[item]);//打印的值
            }
            //遍历字典的值
            foreach (var item in dic1.Values)
            {
                Debug.Log(item);//打印字典的值
            }
            //遍历字典的键值对
            foreach (KeyValuePair<string,string> item in dic1)
            {
                Debug.Log(item.Key);//打印字典的键
                Debug.Log(item.Value);//打印字典的值
            }
    
    
            //list里是否有“1”这个元素
            if (list.Contains("1"))
            {
            }
            //判断字典里是否有“a”这个键
            if (dic1.ContainsKey("a"))
            {
            }
            string value = "";
            //这个方法的返回值,这个字典里有没有第一个参数的这个键
            //如果有,那么把这个键所对应的值放在out参数的value里
            if (dic1.TryGetValue("a", out value))
            {
            }
    
    
            //删除list的所有元素
            list.Clear();
            //删除字典的所有元素
            dic1.Clear();
        }
    }

    专门负责加载AB包的管理器

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class LoadManager
    {
        #region 单例
        private static LoadManager instance;
        public static LoadManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new LoadManager();
                }
                return instance;
            }
        }
        private LoadManager()
        {
            abDic = new Dictionary<string, AssetBundle>();
            abPath = Application.streamingAssetsPath + "/AssetBundle/";
            singleABName = "AssetBundle";
        }
        #endregion
        /// <summary>
        /// 用来存储已经加载的AB包,键是AB包的名字,值就是AB包。
        /// </summary>
        public Dictionary<string, AssetBundle> abDic;
        //用来存储单一的ab包
        public AssetBundle singleAB;
        //单一的构建清单,所有的ab包的依赖全部从这获取
        public AssetBundleManifest singleManifest;
        //存储ab包的路径
        public string abPath;
        //单一的ab包的名字
        public string singleABName;
        /// <summary>
        /// 加载单一的ab包,和单一的构建清单
        /// </summary>
        private void LoadSingleAssetBundle()
        {
            //每次加载单一的ab包需要判断是否加载果过。
            //singleAB为null没加载过,不为null就是加载过
            if (singleAB == null)
            {
                singleAB = AssetBundle.LoadFromFile(abPath + singleABName);
            }
            //从单一的ab包中加载单一的构建清单
            if (singleManifest == null)
            {
                singleManifest = singleAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            }
        }
        /// <summary>
        /// 加载指定ab包的所有的依赖项
        /// </summary>
        /// <param name="abName"></param>
        private void LoadAllDependencies(string abName)
        {
            LoadSingleAssetBundle();
            //首先先获取指定的这个ab包的所有的依赖项
            //从单一的构建清单中获取
            string[] deps = singleManifest.GetAllDependencies(abName);
            //遍历去加载依赖项
            for (int i = 0; i < deps.Length; i++)
            {
                //加载该依赖项前,先判断之前加载没加载过该依赖项
                //就是判断存储ab包的字典里有没有这个ab包
                if (!abDic.ContainsKey(deps[i]) )
                {
                    //如果未加载过,需要加载
                    AssetBundle ab = AssetBundle.LoadFromFile(abPath + deps[i]);
                    //ab包加载完之后,把加载来的ab包存储在字典里
                    abDic[deps[i]] = ab;
                }
            }
        }
        /// <summary>
        /// 加载指定的ab包,并且返回该ab包
        /// </summary>
        /// <param name="abName"></param>
        /// <returns></returns>
        public AssetBundle LoadAssetBundle(string abName)
        {
            LoadAllDependencies(abName);
            //加载指定的ab包
            //加载前先判断是否已经加载过,如果加载过,把加载过的ab包给你
            //如果未加载过,就加载该ab包
            //方法一:
            //if (abDic.ContainsKey(abName))//证明该ab包已经加载过
            //{
            //    return abDic[abName];
            //}
            //AssetBundle ab = ab = AssetBundle.LoadFromFile(abPath + abName);
            //abDic[abName] = ab;
            //return ab;
            //方法二:优化重构
            AssetBundle ab = null;
            if (!abDic.TryGetValue(abName, out ab))
            {
                //如果进入到这,证明该键没有指定的值,那么证明该ab包未加载,需要加载
                ab = AssetBundle.LoadFromFile(abPath + abName);
                //把加载进来的ab包添加到字典中
                abDic[abName] = ab;
            }
            return ab;
        }
        /// <summary>
        /// 加载指定的ab包中的指定名字的指定类型的资源
        /// </summary>
        /// <typeparam name="T">指定资源的类型</typeparam>
        /// <param name="abName">ab包的名字</param>
        /// <param name="assetName">资源的名字</param>
        /// <returns></returns>
        public T LoadAssetByAB<T>(string abName, string assetName) where T : Object
        {
            //先获取指定的ab包
            AssetBundle ab = LoadAssetBundle(abName);
            if (ab != null)
            {
                return ab.LoadAsset<T>(assetName);
            }
            else
            {
                Debug.LogError("指定的ab包的名字有误!");
            }
            return null;
        }
        /// <summary>
        /// 卸载指定的ab包
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="unloadAllloadedObjects"></param>
        public void UnloadAssetBundle(string abName, bool unloadAllloadedObjects)
        {
            //方法一:
            //if (abDic.ContainsKey(abName))
            //{
            //    abDic[abName].Unload(unloadAllloadedObjects);
            //    abDic.Remove(abName);
            //}
            //方法二:优化重构
            //先判断有没有这个ab包
            AssetBundle ab = null;
            if (abDic.TryGetValue(abName, out ab))
            {
                //卸载ab包
                ab.Unload(unloadAllloadedObjects);
                //从容器中删除该ab包
                abDic.Remove(abName);
            }
        }
        /// <summary>
        /// 卸载全部的ab包
        /// </summary>
        /// <param name="unloadAllloadedObjects"></param>
        public void UnloadAllAssetBundle(bool unloadAllloadedObjects)
        {
            //遍历每一个ab包,调用ab包的卸载的方法
            //遍历键,通过键去获取值进行卸载
            foreach (var item in abDic.Keys)
            {
                abDic[item].Unload(unloadAllloadedObjects);
            }
            //直接遍历值去卸载
            foreach (var item in abDic.Values)
            {
                item.Unload(unloadAllloadedObjects);
            }
            abDic.Clear();
        }
    }

    测试管理器

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class TestManager : MonoBehaviour {
        public UnityEngine.UI.Image image;
           // Use this for initialization
           void Start () {
            //先加载指定ab包,再加载包中的指定名字的指定类型的资源
            //AssetBundle ab = LoadManager.Instance.LoadAssetBundle("prefab");
            //GameObject prefab = ab.LoadAsset<GameObject>("Cube");
            //直接加载指定的ab包中的指定名字的指定类型的资源
            GameObject prefab = LoadManager.Instance.LoadAssetByAB<GameObject>("prefab", "Cube");
            Instantiate(prefab, transform);
            //AssetBundle ab1 = LoadManager.Instance.LoadAssetBundle("prefab");//重复加载也不会报错
            //AssetBundle ui = LoadManager.Instance.LoadAssetBundle("ui");
            //Sprite sp = ui.LoadAsset<Sprite>("beijing_02");
            Sprite sp = LoadManager.Instance.LoadAssetByAB<Sprite>("ui", "beijing_02");
            image.sprite = sp;
        }
           
           // Update is called once per frame
           void Update () {
                  
           }
    }
    热更新的流程(判断是否进行热更新)
    1、先把最新的资源打包成AB包,把最新的AB包上传到服务器上,并且修改服务器端资源版本号。
    2、客户端一启动游戏,首先判断本地的版本号与服务器的版本号是否一致。
    3、如果不一致,从服务器下载最新的资源AB包替换本地的AB包。
    4、 解析最新的AB包从中加载想要的资源。
  • 相关阅读:
    LintCode "Maximum Gap"
    LintCode "Wood Cut"
    LintCode "Expression Evaluation"
    LintCode "Find Peak Element II"
    LintCode "Remove Node in Binary Search Tree"
    LintCode "Delete Digits"
    LintCode "Binary Representation"
    LeetCode "Game of Life"
    LintCode "Coins in a Line"
    LintCode "Word Break"
  • 原文地址:https://www.cnblogs.com/vuciao/p/10363692.html
Copyright © 2011-2022 走看看