公司动态

Unity动态加载Texture2D全攻略:从本地到网络,原理、实战与性能优化

📅 2026/8/1 17:49:50
Unity动态加载Texture2D全攻略:从本地到网络,原理、实战与性能优化
1. 项目概述为什么动态加载Texture2D是Unity开发者的必修课在Unity3D项目里处理图片资源是家常便饭。无论是UI图标、角色贴图还是场景背景最终在引擎里跑起来的大多都是Texture2D对象。新手开发者最习惯的做法就是把所有图片一股脑儿拖进Assets文件夹然后在Inspector面板里拖拽赋值。这种做法在小项目或者原型阶段没问题但一旦项目规模上来问题就全暴露了启动加载慢、内存占用高、热更新困难。这时候“动态加载”就成了必须掌握的技能。所谓动态加载就是不依赖编辑器拖拽赋值而是在运行时通过代码从指定的路径可能是本地StreamingAssets、Resources文件夹也可能是远程服务器读取图片文件并实时创建或更新Texture2D对象。这听起来简单但里面门道不少。比如不同平台Windows、Android、iOS的路径规则天差地别加载大图时处理不当轻则卡顿重则崩溃从网络加载还要考虑异步、缓存、错误重试等一系列问题。我最近在优化一个资源量较大的项目就深度折腾了一遍Texture2D的动态加载。网上教程虽多但要么只讲本地加载要么代码片段零散不成体系还有的用了过时的API。所以我把自己趟过的路、踩过的坑结合最新的Unity版本以2022 LTS为例和最佳实践整理成这篇教程。目标是让你看完后不仅能写出可用的动态加载代码更能理解背后的原理和性能考量真正应用到你的项目里。2. 核心原理与方案选型从文件字节到屏幕像素的旅程在动手写代码之前我们必须搞清楚一个核心问题一张存储在硬盘上的图片文件比如.jpg, .png是如何变成Unity里那个可以被材质球使用的Texture2D对象的这个过程我们称之为“反序列化”或“解码”。2.1 Texture2D的本质与加载流程Texture2D是Unity引擎中用于表示二维纹理的核心类。它并不直接存储图片文件本身而是存储了经过GPU理解的、用于渲染的纹理数据。动态加载的核心任务就是搭建一座桥梁把磁盘文件或网络数据转换成Texture2D内部可用的数据。一个完整的动态加载流程通常包含以下几步获取原始数据从目标路径本地文件系统或网络URL读取原始的二进制字节流byte[]。这是最底层的数据。解码为颜色信息将JPEG、PNG等压缩格式的字节流解码成每个像素的RGBA颜色值。Unity提供了ImageConversion类来帮助我们完成这个工作。创建与填充Texture2D创建一个指定尺寸的Texture2D对象然后将解码得到的像素颜色数据填充进去。应用与优化根据需求设置纹理的过滤模式、循环模式并可能上传到GPU。对于需要频繁更新的纹理还需要考虑如何高效地重用Texture2D对象避免反复创建销毁带来的GC垃圾回收压力。2.2 不同加载源的方案对比与选型根据图片资源的来源我们可以将动态加载分为三大场景每种场景都有其适用的API和注意事项。场景一从本地特定路径加载如StreamingAssets这是最经典、最稳定的动态加载方式。StreamingAssets文件夹在打包后会被原封不动地包含在应用包体内且在不同平台上有固定的可读路径。它适合存放那些不需要加密、但又不希望被打包进AssetBundle或Resources的“原始数据”比如配置文件、视频、以及我们这里讨论的图片。优点路径固定访问速度快兼容性极好。缺点内容在包体内无法在应用安装后更新除非整个应用更新。关键技术使用System.IO命名空间下的File.ReadAllBytes来读取字节再结合ImageConversion.LoadImage进行解码。场景二从Resources文件夹加载严格来说这不算完全的“动态”加载因为它仍然依赖于Unity的序列化系统。你需要把图片放在名为Resources的文件夹或其子文件夹下。通过Resources.LoadTexture2D(“path/without/extension”)来加载。Unity会在后台管理这些资源的加载和卸载。优点使用简单Unity自动管理依赖和内存。缺点Resources文件夹有诸多限制如影响启动时间、所有资源打包在一个大文件中、难以进行精细化的内存管理和热更新。官方已不推荐大量使用。选型建议仅用于必须随包体发布、且数量极少的核心资源如默认Logo、关键UI框架图。对于需要动态管理的图片应避免使用。场景三从网络URL加载这是实现资源热更新的核心。将图片放在你自己的服务器或CDN上客户端在需要时下载。优点实现了资源的动态更新无需发布新版本客户端。缺点受网络环境影响大需要处理异步、超时、错误、缓存等复杂逻辑。关键技术使用UnityWebRequest新版或WWW旧版已过时来发起网络请求获取数据。UnityWebRequestTexture是一个专门用于下载纹理的便捷类但它内部也是走UnityWebRequest的流程。注意API的演进。如果你看到还在使用WWW类的教程请谨慎参考。UnityWebRequest是更现代、更可控的替代方案支持进度回调、更好的错误处理和更灵活的数据流管理。最终选型思路 对于本教程我们将重点覆盖场景一本地StreamingAssets和场景三网络URL这两种最具实用价值和挑战性的方案。我们会从最简单的本地加载开始逐步深入到复杂的网络异步加载与缓存管理确保你能应对实际开发中的大多数需求。3. 实战演练一从StreamingAssets本地加载图片让我们从最基础、最可靠的本地加载开始。假设我们的项目里有一个StreamingAssets文件夹里面放着一张名为“hero_icon.png”的图片。3.1 环境与路径准备首先在Unity项目的Assets目录下创建一个名为StreamingAssets的文件夹。Unity会特殊对待这个文件夹。然后把你的测试图片hero_icon.png放进去。接下来是关键一步获取正确的绝对路径。StreamingAssets的路径在不同平台上是不同的我们不能硬编码。Unity提供了Application.streamingAssetsPath这个属性来获取它。using UnityEngine; using System.IO; public class LocalTextureLoader : MonoBehaviour { void Start() { string fileName hero_icon.png; // 拼接出完整的文件路径 string filePath Path.Combine(Application.streamingAssetsPath, fileName); Debug.Log(文件路径: filePath); // 在Windows编辑器下输出可能类似文件路径: C:/YourProject/Assets/StreamingAssets/hero_icon.png // 在Android真机上输出可能类似文件路径: jar:file:///data/app/.../base.apk!/assets/hero_icon.png } }这里使用了System.IO.Path.Combine来拼接路径这比直接用字符串加号更规范能自动处理不同操作系统的路径分隔符问题。实操心得平台路径差异。在Unity编辑器和Windows/Mac桌面平台Application.streamingAssetsPath返回的是文件系统绝对路径可以直接用File.ReadAllBytes读取。但在Android和iOS平台情况特殊Android打包后APK内的资源文件并非普通文件而是一个压缩包APK本质上是个ZIP。Application.streamingAssetsPath返回的是一个形如“jar:file:///...”的URI你不能直接用System.IO.File去读。在Android上读取StreamingAssets必须使用UnityWebRequest或WWW尽管后者已过时。这是新手最容易踩的坑iOS路径是普通的文件系统路径可以直接读取。 因此一个健壮的本地加载方法必须进行平台判断。3.2 实现跨平台的本地加载方法下面是一个兼容各平台的、从StreamingAssets加载Texture2D的完整方法using UnityEngine; using UnityEngine.Networking; using System.IO; using System.Collections; public class LocalTextureLoader : MonoBehaviour { public Renderer targetRenderer; // 用于显示图片的Renderer IEnumerator Start() { string fileName hero_icon.png; string filePath Path.Combine(Application.streamingAssetsPath, fileName); Texture2D texture null; // 平台判断 if (Application.platform RuntimePlatform.Android) { // Android平台必须使用UnityWebRequest using (UnityWebRequest uwr UnityWebRequestTexture.GetTexture(filePath)) { yield return uwr.SendWebRequest(); if (uwr.result ! UnityWebRequest.Result.Success) { Debug.LogError(加载纹理失败: uwr.error); } else { texture DownloadHandlerTexture.GetContent(uwr); } } } else { // 其他平台Windows, Mac, iOS等可以直接读取文件 if (File.Exists(filePath)) { byte[] fileData File.ReadAllBytes(filePath); texture new Texture2D(2, 2); // 创建一个小纹理LoadImage会重置尺寸 bool isLoaded texture.LoadImage(fileData); // 自动解码png/jpg等 if (!isLoaded) { Debug.LogError(解码图片数据失败.); Destroy(texture); texture null; } } else { Debug.LogError(文件不存在: filePath); } } // 应用纹理 if (texture ! null) { // 设置纹理参数非必须但建议 texture.wrapMode TextureWrapMode.Clamp; // 或 Repeat根据需求 texture.filterMode FilterMode.Bilinear; if (targetRenderer ! null) { targetRenderer.material.mainTexture texture; } Debug.Log(本地图片加载成功尺寸: texture.width x texture.height); } } }代码关键点解析平台分支核心逻辑就是判断是否为Android是则用UnityWebRequestTexture否则用File.ReadAllBytes。Texture2D.LoadImage(byte[] data)这是一个非常方便的方法它接受图片文件如PNG, JPG的原始字节数组自动完成解码并填充到Texture2D中。它会根据图片实际尺寸重新设置调用它的Texture2D对象的尺寸。所以我们创建时可以用任意尺寸如2x2。using语句与UnityWebRequestUnityWebRequest实现了IDisposable接口使用using语句可以确保网络请求对象在使用完毕后被正确销毁释放网络连接等非托管资源避免内存泄漏。这是重要的好习惯。纹理设置加载成功后我们设置了wrapMode和filterMode。Clamp模式会让纹理边缘拉伸适合UI图标Repeat模式会平铺适合地面、墙壁等贴图。Bilinear是双线性过滤在纹理缩放时能提供较好的平滑效果。3.3 性能优化与内存管理即使对于本地加载性能也不容忽视。避免在每帧创建/销毁如果需要频繁切换图片如相册浏览不要每次加载都new Texture2D然后赋值最后又Destroy。更好的做法是复用同一个Texture2D对象。你可以预先创建一个足够大的Texture2D或根据最大可能尺寸创建后续加载新图片时调用Texture2D.LoadImage它会复用这个纹理对象并更新其像素数据。这能显著减少GC垃圾回收次数。private Texture2D _reusableTexture; // 成员变量 void LoadNewImageToExistingTexture(byte[] data) { if (_reusableTexture null) { _reusableTexture new Texture2D(2, 2); } _reusableTexture.LoadImage(data); // ... 应用 _reusableTexture }及时卸载当确定某张纹理不再需要时如切换场景主动调用Destroy(texture)。虽然未被引用的纹理最终会被GC清理但纹理内存占用大主动管理能更及时地释放资源。对于从Resources.Load加载的纹理应使用Resources.UnloadAsset。4. 实战演练二从网络URL异步加载与高级管理从网络加载图片是现代游戏和应用的标配用于头像、宣传图、道具图标等的更新。这比本地加载复杂得多核心挑战在于异步和不确定性网络慢、失败、图片格式错误等。4.1 基础网络加载实现我们使用UnityWebRequestTexture来简化操作它内部会处理好纹理的创建和解码。using UnityEngine; using UnityEngine.Networking; using System.Collections; public class NetworkTextureLoader : MonoBehaviour { public string imageUrl https://your-image-server.com/hero_icon.jpg; public Renderer targetRenderer; IEnumerator Start() { yield return StartCoroutine(LoadTextureFromWeb(imageUrl)); } public IEnumerator LoadTextureFromWeb(string url) { using (UnityWebRequest uwr UnityWebRequestTexture.GetTexture(url)) { // 可以设置超时可选但很重要 uwr.timeout 10; yield return uwr.SendWebRequest(); if (uwr.result UnityWebRequest.Result.ConnectionError || uwr.result UnityWebRequest.Result.ProtocolError) { Debug.LogError($网络加载失败: {uwr.error}, URL: {url}); // 这里可以触发一个加载失败的默认图标显示 } else { Texture2D downloadedTexture DownloadHandlerTexture.GetContent(uwr); if (downloadedTexture ! null) { ApplyTexture(downloadedTexture); Debug.Log($网络图片加载成功: {url}, 尺寸: {downloadedTexture.width}x{downloadedTexture.height}); } } } } void ApplyTexture(Texture2D texture) { texture.wrapMode TextureWrapMode.Clamp; texture.filterMode FilterMode.Bilinear; if (targetRenderer ! null) { targetRenderer.material.mainTexture texture; } // 在实际项目中这里可能是一个事件通知UI系统更新图片显示 } }关键点协程CoroutineUnityWebRequest.SendWebRequest()是一个异步操作必须配合yield return在协程中等待其完成。不能在普通Update方法里直接调用。错误处理必须检查uwr.result。ConnectionError代表网络连接问题如无网络ProtocolError代表服务器响应错误如404未找到。详细的错误信息在uwr.error中。超时设置uwr.timeout很重要。网络状况不佳时如果没有超时请求可能会挂起很久。10秒是一个比较合理的默认值。获取纹理成功下载后通过DownloadHandlerTexture.GetContent(uwr)获取生成的Texture2D对象。这个处理程序专门用于下载纹理。4.2 实现带内存缓存的加载器每次都从网络下载图片是不可接受的既浪费用户流量又影响体验。我们必须引入缓存机制。一个简单的内存缓存实现如下using UnityEngine; using System.Collections.Generic; public class TextureCacheManager : MonoBehaviour { public static TextureCacheManager Instance; // 单例模式方便全局访问 private Dictionarystring, Texture2D _textureCache new Dictionarystring, Texture2D(); private Dictionarystring, ListSystem.ActionTexture2D _loadingCallbacks new Dictionarystring, ListSystem.ActionTexture2D(); void Awake() { if (Instance null) { Instance this; DontDestroyOnLoad(gameObject); // 跨场景不销毁 } else { Destroy(gameObject); } } public void GetTexture(string url, System.ActionTexture2D onLoaded) { // 1. 检查内存缓存 if (_textureCache.TryGetValue(url, out Texture2D cachedTex)) { onLoaded?.Invoke(cachedTex); return; } // 2. 检查是否正在加载中防止同一URL重复请求 if (_loadingCallbacks.ContainsKey(url)) { _loadingCallbacks[url].Add(onLoaded); return; } // 3. 创建新的加载任务 _loadingCallbacks[url] new ListSystem.ActionTexture2D(); _loadingCallbacks[url].Add(onLoaded); StartCoroutine(DownloadAndCacheTexture(url)); } private IEnumerator DownloadAndCacheTexture(string url) { // ... 使用上面的 UnityWebRequestTexture 下载逻辑 ... Texture2D downloadedTexture null; using (UnityWebRequest uwr UnityWebRequestTexture.GetTexture(url)) { uwr.timeout 10; yield return uwr.SendWebRequest(); if (uwr.result UnityWebRequest.Result.Success) { downloadedTexture DownloadHandlerTexture.GetContent(uwr); if (downloadedTexture ! null) { // 存入内存缓存 _textureCache[url] downloadedTexture; } } else { Debug.LogWarning($缓存下载失败: {url}, Error: {uwr.error}); // 可以在这里存入一个“加载失败”的占位纹理避免下次继续尝试失败请求 } } // 通知所有等待这个URL的回调 if (_loadingCallbacks.TryGetValue(url, out var callbacks)) { foreach (var callback in callbacks) { callback?.Invoke(downloadedTexture); // 成功传纹理失败传null } _loadingCallbacks.Remove(url); // 清理回调列表 } } // 提供清理缓存的方法在内存紧张时调用 public void ClearCache() { foreach (var tex in _textureCache.Values) { Destroy(tex); } _textureCache.Clear(); Debug.Log(纹理缓存已清空); } }使用方式// 在任何需要加载网络图片的地方 TextureCacheManager.Instance.GetTexture(imageUrl, (texture) { if (texture ! null) { // 加载成功更新UI或模型 myImage.sprite Sprite.Create(texture, new Rect(0,0,texture.width, texture.height), Vector2.one * 0.5f); } else { // 加载失败显示默认图 myImage.sprite defaultSprite; } });缓存设计解析内存缓存Dictionary用URL作为KeyTexture2D作为Value。第二次请求相同URL时直接返回速度极快。回调队列这是防止重复请求的关键。当第一个请求发出后后续对同一URL的请求不会发起新的网络请求而是将其回调函数加入一个列表。当第一个请求完成成功或失败后统一通知列表中的所有回调。这在高并发场景如列表快速滑动加载头像下至关重要。生命周期管理缓存管理器以单例形式存在并DontDestroyOnLoad使得缓存可以在不同场景间共享。同时提供了ClearCache方法可以在收到内存警告如Application.lowMemory事件或特定时机如切换大关卡时主动清理防止内存无限增长。4.3 引入磁盘缓存持久化内存缓存解决了同一运行会话内的重复加载问题但应用重启后缓存就没了。为了更好的用户体验和节省流量我们需要将下载的图片保存到本地磁盘下次启动直接读取。思路是当网络下载成功后除了放入内存缓存还将图片的字节数据uwr.downloadHandler.data写入到本地一个特定目录如Application.persistentDataPath下的某个文件夹。下次请求时先检查内存缓存再检查磁盘是否有缓存文件最后才走网络。关键步骤生成缓存Key不能直接用URL作为文件名可能包含非法字符。通常对URL进行哈希如MD5生成一个短字符串作为文件名。缓存目录管理在Application.persistentDataPath下创建自己的缓存文件夹如ImageCache。读写文件使用File.WriteAllBytes保存File.ReadAllBytes读取。注意要在子线程或异步任务中进行IO操作避免主线程卡顿Unity的FileAPI在主线程是同步的对于大文件可能卡顿可以考虑用Task.Run包裹。缓存过期与清理可以为缓存文件记录下载时间实现LRU最近最少使用清理策略或者设置一个总大小上限定期清理最旧的文件。由于实现代码较长这里给出核心逻辑伪代码string cacheKey GenerateCacheKey(url); // 例如 MD5(url) string cacheFilePath Path.Combine(persistentCachePath, cacheKey .cache); // 加载优先级内存 - 磁盘 - 网络 if (内存缓存命中) return; if (File.Exists(cacheFilePath)) { byte[] fileData File.ReadAllBytes(cacheFilePath); // 用 LoadImage 创建纹理加入内存缓存 return; } // 走网络下载... // 下载成功后 File.WriteAllBytes(cacheFilePath, downloadedData); // 保存到磁盘5. 常见问题、性能陷阱与排查技巧动态加载纹理看似简单但实际项目中会遇到各种稀奇古怪的问题。下面是我总结的一些高频问题和解决方案。5.1 纹理加载后“变粉”或显示不正确这是最经典的问题通常表现为纹理显示成全粉色Missing材质或颜色错乱。原因1异步加载未完成就使用。你启动了协程加载但在yield return完成前就去访问了那个可能还是null的Texture2D变量。解决确保所有使用纹理的逻辑如赋值给material.mainTexture都在协程完成后的回调中或者使用async/awaitUnity 2017.1配合UnityWebRequest.SendWebRequest().Send()的Task形式。原因2纹理格式不匹配。你用new Texture2D(width, height)创建纹理时指定的纹理格式如TextureFormat.RGBA32与你要加载的图片数据格式不兼容。LoadImage方法会自动转换但如果你是自己操作texture.SetPixels就需要格外注意。解决对于从文件加载优先使用LoadImage。对于从原始数据创建确保Texture2D构造时的格式与数据匹配。原因3Android平台路径错误。在Android上试图用File.ReadAllBytes去读Application.streamingAssetsPath下的文件必然失败。解决严格进行平台判断Android上用UnityWebRequest。原因4纹理未被正确上传至GPU。在某些极少数情况下特别是动态创建的纹理可能需要手动调用texture.Apply()来将CPU端的像素数据上传到GPU。解决在修改了纹理的像素数据通过SetPixels,LoadImage等后调用texture.Apply()。注意LoadImage内部会自动调用Apply。5.2 内存泄漏与GC压力纹理是内存消耗大户管理不善会导致应用闪退或卡顿。问题不断new Texture2D从未Destroy。每次加载都创建新对象旧的对象虽然失去了引用但Unity不会立即释放纹理GPU内存直到GC触发。这会导致内存峰值过高。解决复用纹理对象如前所述对于频繁更换的图片使用同一个Texture2D对象调用LoadImage。主动销毁当确定一个纹理不再需要如UI关闭、角色死亡如果它不是复用的就主动调用Destroy(texture)。对于从Resources.Load加载的用Resources.UnloadAsset(texture)。缓存管理使用缓存避免相同资源的重复加载。同时缓存要有清理策略不能只增不减。问题协程泄漏。启动了一个网络加载协程但在加载完成前承载这个协程的GameObject被销毁了。协程可能不会自动停止或者回调引用了已被销毁的对象导致错误或资源无法释放。解决在MonoBehaviour的OnDestroy方法中使用StopAllCoroutines()。在协程的回调中使用if (this null) yield break;或检查GameObject的引用是否有效。考虑使用更健壮的异步方案如基于UniTask等第三方库它们提供了更好的取消和生命周期管理。5.3 网络加载的稳定性问题问题弱网环境下超时或失败。解决合理设置超时根据图片重要性设置不同超时如头像5秒背景图10秒。实现重试机制对于重要资源可以在失败后延迟几秒重试1-2次。int retryCount 0; int maxRetry 2; while (retryCount maxRetry) { // ... 尝试下载 ... if (成功) break; retryCount; if (retryCount maxRetry) { Debug.Log($第{retryCount}次重试...); yield return new WaitForSeconds(2.0f); // 等待2秒后重试 } }提供降级方案加载失败时显示一个本地的默认占位图Placeholder并记录日志等网络恢复后可能再尝试。问题大量并发请求导致卡顿或崩溃。解决实现一个请求队列或连接池。不要同时发起几十上百个网络请求。可以设置一个最大并发数如4-6个将超出数量的请求放入队列等有请求完成后再从队列中取出新的执行。这能有效减轻网络层和游戏帧率的压力。5.4 平台特定问题速查表平台常见问题解决方案与排查点AndroidStreamingAssets路径无法用File读取。使用UnityWebRequest加载Application.streamingAssetsPath下的文件。Android网络权限不足。确保AndroidManifest.xml中有uses-permission android:nameandroid.permission.INTERNET /。iOS对非HTTPS的HTTP链接请求失败。iOS强制要求ATSApp Transport Security。要么使用HTTPS链接要么在Xcode工程中配置Info.plist允许HTTP例外仅限测试上架需谨慎。WebGL跨域请求CORS被浏览器拦截。服务器必须正确配置CORS响应头如Access-Control-Allow-Origin: *。对于本地文件WebGL无法直接访问文件系统网络加载是唯一途径。所有平台编辑器下正常打包后失败。检查文件是否真的被打包如StreamingAssets中的文件大小。检查路径拼接是否正确注意大小写Linux/Android系统区分大小写。使用Debug.Log输出完整路径进行比对。6. 进阶性能监控、工具与最佳实践当你的动态加载系统稳定运行后下一步就是优化和监控。6.1 使用Unity Profiler监控纹理内存Unity Profiler是性能分析的利器。在Profiler的Memory模块中关注Texture2D的内存占用。查看总量确保纹理内存在一个合理范围内根据项目目标设备而定。识别泄露反复进行“打开界面-关闭界面”的操作观察纹理内存是否每次都能回落。如果只增不减说明存在泄漏。查看具体纹理在Detailed视图下可以按内存排序找到占用最大的单个纹理检查其加载和卸载逻辑。6.2 纹理压缩与尺寸优化动态加载的纹理其尺寸和格式直接影响内存和加载速度。尺寸非越大越好加载一张2048x2048的图片作为64x64的UI图标是巨大的浪费。在服务器端或导入时就应根据最终显示尺寸提供不同分辨率的图片或使用Unity的Sprite Atlas进行管理。利用平台压缩格式对于UI精灵Sprite在Unity中设置为Sprite (2D and UI)并选择合适的压缩格式如Android用ETC2/ASTCiOS用PVRTC/ASTC可以大幅减少运行时内存。但注意从网络或本地动态加载的图片Unity无法对其进行这种平台特定的压缩它们会以未压缩的RGBA32格式存在于内存中占用很大空间。解决方案对于需要大量动态加载的UI图标一个高级方案是在打包时预生成一个包含所有可能图标的Sprite图集AssetBundle动态加载时实际加载的是这个图集AssetBundle然后通过Sprite名称来索引。这样就能享受纹理压缩的好处。但这套方案比较复杂涉及AssetBundle的管理。6.3 异步加载与用户体验不要让用户盯着空白等待。显示加载进度UnityWebRequest有downloadProgress属性0到1可以用来更新进度条。使用占位图在加载完成前先显示一个低分辨率的占位图或旋转的Loading图标。预加载在进入一个场景前如加载界面提前加载该场景可能用到的主要网络图片。懒加载与卸载对于列表类内容如背包、商城只加载当前可视区域及前后缓冲区的图片。当物品滚出视野后可以延迟一段时间再销毁其纹理如果它很快又滚回来就能直接从内存缓存中读取。动态加载Texture2D是Unity开发中一项从入门到精通的技能。从简单的文件读取到复杂的网络缓存、内存管理、性能优化每一步都需要根据项目实际情况仔细权衡。我个人的经验是在项目初期就搭建一个健壮的、可扩展的纹理加载管理框架远比后期在性能问题和奇怪的Bug面前缝缝补补要高效得多。这套框架的核心无非是缓存、异步、错误处理、资源生命周期管理。把这几块做扎实了项目中关于图片处理的绝大多数需求就都能从容应对了。