Unity安卓端如何实现从内部存储无缝加载图片?性能优化求助
Unity安卓拼图游戏本地图片加载优化方案
现有代码核心问题
- 用
UnityWebRequest加载本地存储文件,额外引入了网络请求模块的开销,加载速度慢 - 无内存缓存,滚动视图中同一张图反复销毁、重复从本地读取解码,IO开销冗余
- 未做纹理尺寸适配,原始高清图直接加载进内存,安卓设备内存阈值低,极易出现内存不足加载失败的问题
- 频繁调用
GetComponent获取组件,无提前缓存 - 存储的是原始JPG/PNG资源,每次加载都要做全量解码,CPU开销高
具体优化措施
1. 替换本地文件加载逻辑
直接删除UnityWebRequest加载本地文件的逻辑,改用File.ReadAllBytes+Texture2D.LoadImage读取本地文件,加载速度至少提升30%以上。
2. 新增LRU内存缓存层
全局维护一个最多存储20~30张最近使用纹理的LRU缓存,滚动时显示过的图片不用重复读本地,直接从内存取,同时缓存满了自动释放最久未使用的纹理,避免内存溢出。
3. 加载时强制适配显示尺寸
根据你的关卡缩略图实际显示尺寸(比如256*256),加载纹理的时候强制缩小到对应尺寸,内存占用可降低到原来的1/16甚至更低,解码速度也会大幅提升。
4. 组件提前缓存
在Awake阶段就提前获取当前对象的RawImage组件,不要每次加载图片的时候都调用GetComponent。
5. 分帧加载控制
限制同一时间加载的图片协程数量,最多同时跑2~3个加载协程,避免一帧内大量解码操作造成卡顿。
优化后示例代码
// 全局LRU缓存示例,可放在单例类里,简单实现即可无需引入第三方库 public static LRUCache<string, Texture2D> imageCache = new LRUCache<string, Texture2D>(30); private RawImage _rawImage; private void Awake() { _rawImage = GetComponent<RawImage>(); } public IEnumerator DownloadOrLoadImage(string link, string dirName, int index = -1) { if (!loadFromResources) { int startPos = link.LastIndexOf("https://drive.google.com/file/d/") + "https://drive.google.com/file/d/".Length; int length = link.IndexOf("/view?usp=sharing") - startPos; if (length >= 1) { string ID = link.Substring(startPos, length); string cachePath = Application.persistentDataPath + "/.cache/" + ID; // 先查内存缓存 if (imageCache.TryGet(ID, out Texture2D cachedTex)) { _rawImage.texture = cachedTex; isImageDownloaded = true; if (index != -1 && index == LevelMenuLoader.instance.totalJsonCount) { LevelMenuLoader.instance.AfterMenuDownloadSequence(); } yield break; } if (File.Exists(cachePath)) { if (index != -1 && index == LevelMenuLoader.instance.totalJsonCount) { LevelMenuLoader.instance.AfterMenuDownloadSequence(); } print("Loading from the device"); // 同步读字节,等一帧分散压力避免卡顿 byte[] bytes = File.ReadAllBytes(cachePath); yield return null; if (bytes.Length > 1) { Texture2D tex = new Texture2D(2, 2, TextureFormat.RGB24, false); tex.LoadImage(bytes); // 按需缩放到目标显示尺寸,比如缩略图需要256*256就填256 // ScaleTexture(tex, 256, 256); _rawImage.texture = tex; // 加入内存缓存 imageCache.Put(ID, tex); isImageDownloaded = true; } } else { LevelMenuLoader.instance.AfterMenuDownloadSequence(); print("Downloading from the web"); using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(link)) { yield return request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) { Debug.Log(request.error); } else { Texture2D downloadedTex = ((DownloadHandlerTexture)request.downloadHandler).texture; byte[] bytes = ((DownloadHandlerTexture)request.downloadHandler).data; if (bytes.Length > 1) { // 下载后先缩放到目标尺寸再存储,减少本地文件大小和后续加载开销 // ScaleTexture(downloadedTex, 256, 256); // byte[] compressedBytes = downloadedTex.EncodeToJPG(80); File.WriteAllBytes(cachePath, bytes); _rawImage.texture = downloadedTex; imageCache.Put(ID, downloadedTex); isImageDownloaded = true; } request.downloadHandler.Dispose(); } } } } } else { string resPath = "Levels/" + dirName + "/" + GetComponent<LevelInformation>().m_LevelID; _rawImage.texture = Resources.Load<Texture>(resPath); } } // 纹理缩放辅助方法 private void ScaleTexture(Texture2D source, int targetWidth, int targetHeight) { RenderTexture rt = RenderTexture.GetTemporary(targetWidth, targetHeight); Graphics.Blit(source, rt); RenderTexture.active = rt; source.Reinitialize(targetWidth, targetHeight); source.ReadPixels(new Rect(0, 0, targetWidth, targetHeight), 0, 0); source.Apply(); RenderTexture.ReleaseTemporary(rt); RenderTexture.active = null; }
内容的提问来源于stack exchange,提问作者Bhavin
相关产品推荐
相关产品推荐

