教程-【唐老狮】Unity热更新之AssetBundle 
AB包理论基础 什么是AB包 特定于平台的资产压缩包,类似压缩文件
资产包括:模型、贴图、预设体、音效、材质球等
AB包的作用 相对于Resources下的资源AB包能更好管理资源,resources打包后是只读的,AB包存储位置可自定义,后期可以动态更新
减小包体大小:压缩资源、减少初始包大小 
热更新:资源热更新、脚本热更新 
 
AB包资源打包 1.unity编辑器开发,自定义打包工具 如果想自己写打包工具,学这个
2.官方提供好的打包工具 AssetBundleBrowser
//现在(2023-4)建议使用addressable
解压方式:
LZMA:压缩后文件小,使用其中任何资源都要解压整个包
LZ4:压缩后文件稍大,使用其中某个资源只需打包该资源
 
AB包资源加载 
1 2 3 4 5 6 7 var  ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath+"某个路径" );var  obj1 = ab.LoadAsset<资源类型>("资源名" );var  obj2 = ab.LoadAsset("资源名" ,typepf(资源类型))as  资源类型
1 2 3 4 5 6 7 8 IEnumerator LoadABRes (string  ABName, string  resName {          AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetPath +"某个路径" );          var  abq = abcr.assetBudle.LoadassetAsync("资源名" ,typeof (类型));     yield  return  abq; } 
1 StartCoroutine(LoadABRes("包名" ,"资源名" )); 
1 AssetBundle.UnloadAllAssetBundles(false ); 
AB包依赖 在包中的A资源如果使用了另外一个B资源(A依赖B),会自动的将它打包到同一个包中。
如果A资源和B资源打包到了不同的包中,只加载A资源所在的包,会出现B资源丢失。此时B资源所在的包叫依赖包,需要加载依赖包使A资源正常使用
1 2 3 4 5 6 7 8 9 10 11 12 var  ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath +"某个路径A" )AssetBundle abMain = AssetBundle. LoadFromFile(Application.streamingAssetsPath+"主包路径" ); AssetBundleMainfest abManifest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest" ); string [] strs = abMainifest.GetAllDependencies("路径A的包名" );for (int  i =0 ;i<strs.Length;i++){     AssetBundle.LoadFromFile(Application.streamingAssetsPath +"/" +strs[i]); } 
AB包资源管理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 public  class  ABMgr  : SingletonMono <ABMgr >{                    private  Dictionary<string ,AssetBundle> abDic = new ();               private  string  PathUrl => Application.streamingAssetsPath + "/" ;          private  string  MainABName     {     	get          {             #if  UNITY_IOS                  return  "IOS" ;             #elif  UNITY_ANDROID                  return  "ANDROID" ;             #else                    return  "PC" ;         }     }          private  AssetBundle mainAB = null ;          private  AssetBundleManifest manifest = null ;               public  object  LoadRes (string  abName,string  resName     {         LoadAB(abName);         return  abDic[abName].LoadAsset(resName);     }          public  object  LoadRes (string  abName,string  resName, System.Type type     {         LoadAB(abName);         return  abDic[abName].LoadAsset(resName,type);     }          public  object  LoadRes <T >(string  abName,string  resNamewhere  T:object      {         LoadAB(abName);         return  abDic[abName].LoadAsset<T>(resName);     }          public  void  LoadAB (string  name     {                  if (maniAB = null )         {             mainAB = AssetBundle.LoadFromFile(PathUrl+MainABName);             manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest" );         }                  AssetBundle ab = null ;         string [] strs = manifest.GetAllDependencies(abName);         for (int  i = 0 ; i<strs.Length;i++)         {                          if (!abDic.ContainsKey(strs[i]))             {                 ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);                 abDic.Add(str[i],ab);             }         }                       	if (!abdic.ContainsKey(abName))         {             ab = AssetBunle.LoadFromFile(PathUrl + abName);             abDic.Add(Abname,ab);         }     }                    public  void  LoadResAsync (string  abName,string  resName,UnityAction<Object> callBack     {         StartCoroutine(ReallyLoadResAsync(abName,resName,callBack));     }          private  IEnumerator ReallyLoadResAsync (string  abName,string  resName,UnityAction<Object> callBack     {         LoadAB(abName);         var  abr = abDic[abName].LoadAssetAsync(resName);         yield  return  abr;         callBack(abr.asset);     }              public  void  LoadResAsync (string  abName,string  resName,System.Type type,UnityAction<Object> callBack     {         StartCoroutine(ReallyLoadResAsync(abName,resName,type,callBack));     }          private  IEnumerator ReallyLoadResAsync (string  abName,string  resName,System.type,UnityAction<Object> callBack     {         LoadAB(abName);         var  abr = abDic[abName].LoadAssetAsync(resName,type);         yield  return  abr;         callBack(abr.asset);     }          public  void  LoadResAsync <T >(string  abName,string  resName,UnityAction<T> callBackwhere  T:object      {         StartCoroutine(ReallyLoadResAsync<T>(abName,resName,callBack));     }          private  IEnumerator ReallyLoadResAsync <T >(string  abName,string  resName,UnityAction<T> callBack     {         LoadAB(abName);         var  abr = abDic[abName].LoadAssetAsync<T>(resName);         yield  return  abr;         callBack(abr.asset as  T);              }               public  void  UnLoad (string  abName     {         if (abDic.ContainsKey(abName))         {             abDic[abName].Unload(false );             abDic.Remove(abName);         }     }               public  void  ClearAb ()     {      AssetBundle.UnloadAllAssetBundles(false );         abDic.Clear();         maniAB = null ;         manifest = null ;     } 	 } 
宏定义:在不同的环境下执行不同的代码,比如在安卓平台和在苹果平台下执行不同的代码,这样你就可以专门为你的目标平台编译代码
 
使用AB包资源管理器
1 2 3 4 5 6 7 void  Start (){     var  obj = ABMgr.Instance.LoadRec("model" ,"Cube" );     Instantiate(obj);     obj = ABMgr.Instance.LoadRec("model" ,"Cube" ,typeof (GameObject)) as  GameObject;     obj = ABMgr.Instance.LoadRec<GameObject>("model" ,"Cube" ); }