赞
踩
1.搭建资源服务器使用(HFS软件(https://www.pianshen.com/article/54621708008/))
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- using System;
-
- public class Singleton<T> where T : class, new()
- {
- private static readonly Lazy<T> lazy = new Lazy<T>(() => new T());
- public static T Instance { get { return lazy.Value; } }
- protected Singleton() { }
- }
-
- public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
- {
-
- private static T _instance;
-
- public static T Instance
- {
- get
- {
- return _instance;
- }
- }
-
- protected virtual void Awake()
- {
- _instance = this as T;
- }
- }
-
-
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
2.核心代码
- using Cysharp.Threading.Tasks;
- using System.Collections;
- using System.Collections.Generic;
- using System.Xml.Linq;
- using UnityEngine;
- using UnityEngine.Networking;
- using UnityEngine.U2D;
-
-
-
- /*
- 内存优化之AB包篇(微信小游戏)
- 问题:如何优化AB包占用的内存,游戏的AB包资源卸载策略是什么
- 答:卸载时机
- 1.该AB包被引用次数为0时候,此时缓存一定的时间,当缓存时间为0时,就可以调用bundle.Unload(true);
- 缓存时间内被调用重置缓存时间,引用次数增加。
- 这部分主要用来处理偶尔打开的界面
- 2.首先维护一个已经加载的AB包资源大小总值,然后设置一个内存基准值,当总值大于内存基准值的时候,
- 此时去卸载那些引用次数为0的ab资源(优先卸载加载早的ab包资源)。
- 这部分用来处理短时间内,玩家打开过多大型界面场景,如果不这么处理,手机内存会占用高且发热会严重。
- 引用次数的维护时机(引用次数始终不小于0)
- 1.例如一张图片 更换属于不同ab包的资源图片时,需要先将旧的ab包 引用次数减一,界面销毁时,最后动态加载的ab图片资源也需要减一,其他资源同理
- 2.同时加载一个AB资源时,在AB资源未加载完毕前,需要维护一个加载中的AB包资源实际被加载次数,
- 由于部分界面 正在动态加载的ab包资源未加载完毕时,此界面就可能已经被销毁,如果被销毁就需要将加载中的ab包的实际被加载次数减一。
- 3.当ab包资源加载完毕时,如果发现加载中的此ab包维护的实际被加载次数大于0,此时ab包的引用次数加一,同时实际被加载次数减一。
- 4.当界面销毁时,此界面的ab包和相关依赖的引用次数需要减一,动态加载的ab包资源也需要将引用次数减一
- 5.!!!需要注意的是 当A依赖于B时, A的最后一个实例被销毁时 A的引用变为0 但是B的引用此刻不变,除非A被卸载 才能将B的引用减一
- // A依赖于B
- // A被加载 会先加载B
- //那么A引用为1 B引用为1
- //A被加载第二次 A引用为2 B引用为2
- //A被加载第3次 A引用为3 B引用为3
- // A被删除1次 引用为2 B引用为2
- //A被删除第二次 A引用为1 B引用为1
- //A被删除第3次 A引用为0 B引用为1
- //A被卸载时 B引用为0
- // A依赖于B
- // A被加载 会先加载B
- //那么A引用为1 B引用为1
- //A被加载第二次 A引用为2 B引用为2
- // A被删除1次 引用为1 B引用为1
- //A被删除第二次 A引用为0 B引用为1
- //A被卸载时 B引用为0
- // A依赖于B
- // A被加载 会先加载B
- //那么A引用为1 B引用为1
- // A被删除1次 A引用为0 B引用为1
- //A被卸载时 B引用为0
- */
- [SelectionBase]
- public class LoadingAssetBundle
- {
- private string abName;
- public string GetABName()
- {
- return abName;
- }
-
- private int realLoadedTimesInLoading = 0;//在加载中 被加载的真实次数(也就是剔除那些只加载不使用的部分,例如界面动态加载图片还没加载完毕 这个界面就被销毁了)
- public int GetRealLoadedTimesInLoading()
- {
- return realLoadedTimesInLoading;
- }
-
- public void AddRealLoadedTimesInLoading()
- {
- realLoadedTimesInLoading++;
- }
-
- public void ReduceRealLoadedTimesInLoading()
- {
- realLoadedTimesInLoading--;
- }
-
- public LoadingAssetBundle(string _abName)
- {
- abName = _abName;
- AddRealLoadedTimesInLoading();
- }
- }
-
- [SelectionBase]
- public class LoadedAssetBundle
- {
- private string abName;
- private AssetBundle bundle;
- private float cacheTimeBySenconds = 10;//缓存秒数不同ab可配置
- public float curLastCacheTime = 10;//当前剩余缓存时间
- public int referenceTimes = 0;//引用次数
- public long memoryValue = 0;//ab包大小
- public int loadIndexOrder = 0;//引用顺序 越小代表越早被引用
- private bool isUnload = false;//是否被卸载
-
- public LoadedAssetBundle(string _abName, AssetBundle _bundle, long _memoryValue, int _loadIndexOrder)
- {
- isUnload = false;
- abName = _abName;
- bundle = _bundle;
- memoryValue = _memoryValue;//long size = long.Parse(unityWebRequest.GetResponseHeader("Content-Length"));
- ABManager.Instance.AddMemoryValue(_memoryValue);
- loadIndexOrder = _loadIndexOrder;
- }
-
- public AssetBundle GetAssetBundle()
- {
- return bundle;
- }
-
- public void AddRefer()//添加引用1
- {
- referenceTimes = referenceTimes + 1;
- curLastCacheTime = cacheTimeBySenconds;//重置剩余缓存1时间时间
- }
-
- public int ReduceRefer()//减少引用
- {
- if (referenceTimes > 0) {
- referenceTimes--;
- };
- return referenceTimes;
- }
-
- public void RefreshCacheLastTime(float time)
- {
- if (referenceTimes == 0)
- {
- curLastCacheTime -= time;
- CheckCacheTimeUnload();
- }
- }
-
-
-
- private void CheckCacheTimeUnload()
- {
- if (isUnload) return;
- if (curLastCacheTime <= 0&& referenceTimes == 0) {
- bundle.Unload(true); //卸载时机1
- isUnload = true;
- ABManager.Instance.ReduceMemoryValue(memoryValue);
- ABManager.Instance.RemoveABRequest(abName);
- ABManager.Instance.ReduceDependciedRefer(abName);
- Debug.Log($"curLastCacheTime Unload{abName},Count={ABManager.Instance.cachedLoadedDic.Count}");
- }
- }
- public void CheckOverMemoryUnload(int curMinReferIndexOrder)
- {
- if (isUnload) return;
- if (referenceTimes == 0 && ABManager.Instance.CheckOverMemoryMemoryReferenceValue())//&& curMinReferIndexOrder == loadIndexOrder
- {
- bundle.Unload(true);//卸载时机2
- isUnload = true;
- ABManager.Instance.ReduceMemoryValue(memoryValue);
- ABManager.Instance.RemoveABRequest(abName);
- ABManager.Instance.ReduceDependciedRefer(abName);
- Debug.Log($"Unload{abName}");
- }
- }
-
- public string GetABName()
- {
- return abName;
- }
-
- public bool IsUnLoad()
- {
- return isUnload;
- }
- }
-
-
- public class ABManager : MonoSingleton<ABManager>
- {
-
- public Dictionary<string, LoadedAssetBundle> cachedLoadedDic = new Dictionary<string, LoadedAssetBundle>();
- private Dictionary<string, LoadingAssetBundle> cachedLoadingDic = new Dictionary<string, LoadingAssetBundle>();
- private long memoryReferenceValue= 995406;//内存基准值
- private long curMemoryValue = 0;//内存当前值
- private int curReferIndexOrder = 0;//当前索引
- private int curMinReferIndexOrder = 0;//当前被加载最早的索引
- public void AddMemoryValue(long _memoryValue)
- {
- curMemoryValue = curMemoryValue + _memoryValue;
- //print("curMemoryValue" + curMemoryValue);
- }
- public void ReduceMemoryValue(long _memoryValue)
- {
- //Debug.Log("memoryValue" + _memoryValue);
- curMemoryValue = curMemoryValue - _memoryValue;
- curMinReferIndexOrder++;
- if (curMinReferIndexOrder > curReferIndexOrder)
- {
- curMinReferIndexOrder = curReferIndexOrder;
- }
- }
-
- public bool CheckOverMemoryMemoryReferenceValue()
- {
- return curMemoryValue > memoryReferenceValue;
- }
-
-
- private float checkSpan = 0.3f;
- public float time;
- List<string> removeList = new List<string>();
- public int CachedLoadedCount;
- private void CheckUnLoadCachedLoaded()
- {
- time += Time.fixedDeltaTime;
- if (time > checkSpan)
- {
- time = 0;
- removeList.Clear();
- foreach (var item in cachedLoadedDic)
- {
- if (!cachedLoadingDic.ContainsKey(item.Key))
- {
- item.Value.RefreshCacheLastTime(checkSpan);
- item.Value.CheckOverMemoryUnload(curMinReferIndexOrder);
- if (item.Value.IsUnLoad()) removeList.Add(item.Key);
- }
- }
-
- for (int i = 0; i < removeList.Count; i++)
- {
- print($"removeList={removeList[i]}");
- cachedLoadedDic.Remove(removeList[i]);
- }
- }
- CachedLoadedCount = cachedLoadedDic.Count;
- }
-
-
- // Update is called once per frame
- void FixedUpdate()
- {
- CheckUnLoadCachedLoaded();
- }
-
- private AssetBundle mainAB = null; //主包
- private AssetBundleManifest mainManifest = null; //主包中配置文件---用以获取依赖包
- private string basePath = "http://192.168.31.208/AssetBundles/";
- private string mainABName = "AssetBundles";
-
-
- public Dictionary<string, string> AssetNameToABName = new Dictionary<string, string>();
- public async UniTask<GameObject> LoadAsset(string assetName)
- {
- string abName = assetName.ToLower() + ".ab";
- AssetBundle ab = await LoadABPackage(abName);
- //await UniTask.SwitchToMainThread();
- return ab.LoadAsset<GameObject>(assetName);
- }
-
-
- /// <summary>
- /// 加载图集里面的图片
- /// 案例
- /// Image a = nul;;
- /// if (a != null)
- /// ABManager.Instance.UnloadAsset(a);
- /// a = ABManager.Instance.LoadAtlasSprite(a);
- /// </summary>
- /// <param name="assetName"></param>
- /// <param name="textureName"></param>
- /// <returns></returns>
- public async UniTask<Sprite> LoadAtlasSprite(string assetName, string textureName)
- {
- string abName = assetName.ToLower() + ".ab";
- AssetBundle ab = await LoadABPackage(abName);
- SpriteAtlas spriteAtlas = ab.LoadAsset<SpriteAtlas>(assetName);
- return spriteAtlas.GetSprite(textureName);
- }
-
- //单个包卸载
- public void ReduceRefer(string assetName)
- {
- string abName = assetName.ToLower() + ".ab";
-
- if (cachedLoadingDic.ContainsKey(abName))
- {
- cachedLoadingDic[abName].ReduceRealLoadedTimesInLoading();
-
- }
- else
- {
- //--引用
- if (cachedLoadedDic.ContainsKey(abName))
- {
- int referValue = cachedLoadedDic[abName].ReduceRefer();
-
- // A依赖于B
- // A被加载 会先加载B
- //那么A引用为1 B引用为1
- //A被加载第二次 A引用为2 B引用为2
- //A被加载第3次 A引用为3 B引用为3
- // A被删除1次 引用为2 B引用为2
- //A被删除第二次 A引用为1 B引用为1
- //A被删除第3次 A引用为0 B引用为1
- //A被卸载时 B引用为0
-
- // A依赖于B
- // A被加载 会先加载B
- //那么A引用为1 B引用为1
- //A被加载第二次 A引用为2 B引用为2
- // A被删除1次 引用为1 B引用为1
- //A被删除第二次 A引用为0 B引用为1
- //A被卸载时 B引用为0
-
-
- // A依赖于B
- // A被加载 会先加载B
- //那么A引用为1 B引用为1
- // A被删除1次 A引用为0 B引用为1
- //A被卸载时 B引用为0
-
- if (referValue > 0)
- {
- ReduceDependciedRefer(abName);
- }
- }
- }
-
- }
-
-
- public void ReduceDependciedRefer(string abName)
- {
- string[] dependencies = mainManifest.GetAllDependencies(abName);
- for (int i = 0; i < dependencies.Length; i++)
- {
- if (cachedLoadedDic.ContainsKey(dependencies[i]))
- {
- cachedLoadedDic[dependencies[i]].ReduceRefer();
- }
- }
- }
-
-
-
-
-
-
-
- //加载AB包
- private async UniTask<AssetBundle> LoadABPackage(string abName)
- {
-
- //加载ab包,需一并加载其依赖包。
- if (mainAB == null)
- {
- //获取ab包内容
- mainAB = await DownloadABPackage(mainABName);
- //获取主包下的AssetBundleManifest资源文件(存有依赖信息)
- mainManifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
- }
-
- //根据manifest获取所有依赖包的名称 固定API 保证不丢失依赖
- string[] dependencies = mainManifest.GetAllDependencies(abName);
- if (dependencies.Length > 0)
- {
- var tasks = new List<UniTask>(); // 创建一个任务列表来存储异步操作
- //循环加载所有依赖包
- for (int i = 0; i < dependencies.Length; i++)
- {
- //如果不在缓存则加入
- if (!cachedLoadedDic.ContainsKey(dependencies[i]))
- tasks.Add(LoadABPackage(dependencies[i]));
- else
- {
- cachedLoadedDic[dependencies[i]].AddRefer(); //++引用
- }
- }
- // 使用UniTask.WhenAll等待所有任务完成
- await UniTask.WhenAll(tasks);
- }
-
- //加载目标包 -- 同理注意缓存问题
- if (cachedLoadedDic.ContainsKey(abName))
- {
- cachedLoadedDic[abName].AddRefer(); //++引用
- Debug.Log($"ContainsKey{abName}");
- return (cachedLoadedDic[abName].GetAssetBundle());
- }
- else
- {
-
- await DownloadABPackage(abName);
- Debug.Log($"DownloadABPackage{abName}");
- return (cachedLoadedDic[abName].GetAssetBundle());
- }
- }
-
- //存儲下載操作
- Dictionary<string, UnityWebRequestAsyncOperation> ABRequestOpera = new Dictionary<string, UnityWebRequestAsyncOperation>();
- public void RemoveABRequest(string abname)
- {
- string url = basePath + abname;
- ABRequestOpera[url].webRequest.Dispose();//试试多个异步创建
- ABRequestOpera.Remove(url);
- }
- async UniTask<AssetBundle> DownloadABPackage(string abname)
- {
- if (cachedLoadedDic.ContainsKey(abname))
- {
- cachedLoadedDic[abname].AddRefer();
- return cachedLoadedDic[abname].GetAssetBundle();
- }
-
- string url = basePath + abname;
- Debug.Log(url);
- if (!cachedLoadingDic.ContainsKey(abname))
- {
- cachedLoadingDic.Add(abname, new LoadingAssetBundle(abname));
- }
- else
- {
- cachedLoadingDic[abname].AddRealLoadedTimesInLoading();
- }
- if (!ABRequestOpera.ContainsKey(url))
- {
- UnityWebRequest req = UnityWebRequestAssetBundle.GetAssetBundle(url);
- UnityWebRequestAsyncOperation operation = req.SendWebRequest();
- ABRequestOpera.Add(url, operation);
- }
- await ABRequestOpera[url];
- if (!cachedLoadedDic.ContainsKey(abname))
- {
- curReferIndexOrder++;
- AssetBundle ab = DownloadHandlerAssetBundle.GetContent(ABRequestOpera[url].webRequest);
- long size = long.Parse(ABRequestOpera[url].webRequest.GetResponseHeader("Content-Length"));
- cachedLoadedDic.Add(abname, new LoadedAssetBundle(abname,ab, size, curReferIndexOrder));
-
- }
-
- if (cachedLoadingDic.ContainsKey(abname)&&cachedLoadingDic[abname].GetRealLoadedTimesInLoading() > 0)
- {
- cachedLoadedDic[abname].AddRefer();
- cachedLoadingDic[abname].ReduceRealLoadedTimesInLoading();
- if (cachedLoadingDic[abname].GetRealLoadedTimesInLoading() == 0)
- {
- cachedLoadingDic.Remove(abname);
- }
- }
- return cachedLoadedDic[abname].GetAssetBundle();
- }
-
-
-
- //所有包卸载
- public void UnLoadAll()
- {
- AssetBundle.UnloadAllAssetBundles(false);
- //注意清空缓存
- cachedLoadedDic.Clear();
- cachedLoadingDic.Clear();
- mainAB = null;
- mainManifest = null;
- }
-
-
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
3..打包AB包代码
- using UnityEngine;
- using UnityEditor;
- using System.IO;
- using System;
- using System.Collections.Generic;
-
- /// <summary>
- /// AB包创建
- /// </summary>
- public class CreateAssetBundles : MonoBehaviour
- {
-
- public static string BuildAssetBundlePath = Application.dataPath + "/AssetsPach/AssetBundles";
- [MenuItem("Build/BuildAssetBundles")]
- public static void BuildAssetBundle()
- {
- SetAssetBundle();
- string dir = BuildAssetBundlePath; //相对路径
- if (!Directory.Exists(dir)) //判断路径是否存在
- {
- Directory.CreateDirectory(dir);
- }
- BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget); //这里是第一点注意事项,BuildTarget类型选择WebGL
- AssetDatabase.Refresh();
- Debug.Log("打包完成");
- }
-
- //需要打包的资源目录
- public static string SetAssetBundlePath = Application.dataPath + "/AssetsPach/WortAsset";
- public static void SetAssetBundle()
- {
- string dir = SetAssetBundlePath; //相对路径
-
- AssetDatabase.RemoveUnusedAssetBundleNames();//移除无用的AssetBundleName
- //Debug.LogError(Application.dataPath);//上级路径 F:/TUANJIEProject/My project/Assets
- list_Files = new List<stru_FileInfo>();
- ContinueCheck(dir);
- for (int a = 0; a < list_Files.Count; a++)//
- {
- SetBundleName(list_Files[a].assetPath);
- }
- Debug.Log("生成ab包完成");
- //SetBundleName("Assets/Ship/AC_Enterprise_T01/prefab/AC_Enterprise_T01_M01_ShipMesh.prefab");
-
- }
-
-
- //******资源参数
- static List<stru_FileInfo> list_Files;//文件列表
-
- static string assetBundleName = "ab";
- static string assetBundleVariant = "";
- //int indentation;//缩进等级
- struct stru_FileInfo
- {
- public string fileName;
- public string filePath;//绝对路径
- public string assetPath;//U3D内部路径
- public Type assetType;
- }
-
-
-
-
-
- static void ContinueCheck(string path)
- {
- DirectoryInfo directory = new DirectoryInfo(path);
- FileSystemInfo[] fileSystemInfos = directory.GetFileSystemInfos();//获取文件夹下的文件信息
-
- foreach (var item in fileSystemInfos)
- {
- int idx = item.ToString().LastIndexOf(@"\");
- string name = item.ToString().Substring(idx + 1);
- if (!name.Contains(".meta"))//剔除meta文件
- {
- CheckFileOrDirectory(item, path + "/" + name);
- }
- }
- }
-
- static void CheckFileOrDirectory(FileSystemInfo fileSystemInfo, string path)
- {
- FileInfo fileInfo = fileSystemInfo as FileInfo;
- if (fileInfo != null)
- {
- stru_FileInfo t_file = new stru_FileInfo();
- t_file.fileName = fileInfo.Name;
- t_file.filePath = fileInfo.FullName;
-
- t_file.assetPath = "Assets" + fileInfo.FullName.Replace(Application.dataPath.Replace("/", "\\"), "");//用于下一步获得文件类型
- t_file.assetType = AssetDatabase.GetMainAssetTypeAtPath(t_file.assetPath);
- list_Files.Add(t_file);
- }
- else
- {
- ContinueCheck(path);
- }
- }
-
- static void SetBundleName(string path)
- {
- print(path);
- var importer = AssetImporter.GetAtPath(path);
- string[] strs = path.Split(".");
- string[] dictors = strs[0].Split('/');
- if (importer)
- {
- if (assetBundleVariant != "")
- {
- importer.assetBundleVariant = assetBundleVariant;
- }
- if (assetBundleName != "")
- {
- importer.assetBundleName = path.ToLower() + "." + assetBundleName;
- }
- }
- else
- {
- Debug.Log("importer是空的" + path);//jpg png tga
- }
- }
-
- }
-
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
4.资源如下 几张美女壁纸,每个预设都是一个壁纸和关闭按钮界面挂载了代码
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UI;
-
- public class Panel : MonoBehaviour
- {
- public string asssetName;
- // Start is called before the first frame update
- void Start()
- {
- transform.GetComponentInChildren<Button>().onClick.AddListener(() => {
- //StartCoroutine(TestLoadSize();
-
- UIManager.Instance.DeletePanel(this);
- });
- }
-
- // Update is called once per frame
- void Update()
- {
-
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
4.启动场景和代码
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UI;
-
- public class UIManager : MonoSingleton<UIManager>
- {
- public Transform parent;
- // Start is called before the first frame update
- void Start()
- {
- for (int i = 0; i < transform.childCount; i++)
- {
- int index = i;
- transform.GetChild(i).GetComponent<Button>().onClick.AddListener(() => {
- //StartCoroutine(TestLoadSize();
- print(111);
- DownPanel($"Assets/Assetspach/wortasset/prefabs/Panel{index + 1}.prefab");
- //DownPanel($"Assets/Assetspach/wortasset/prefabs/Panel{index + 1}.prefab");
- });
- }
- }
-
- async void DownPanel(string asssetName)
- {
- GameObject go = await ABManager.Instance.LoadAsset(asssetName);
- GameObject.Instantiate(go, parent).GetComponent<Panel>().asssetName =asssetName;
- }
-
- // Update is called once per frame
- public void DeletePanel(Panel panel)
- {
- ABManager.Instance.ReduceRefer(panel.asssetName);
- DestroyImmediate(panel.gameObject);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。