Unity资源加载
Unity资源分两类:源资源(编辑器工程目录下,.png/.fbx/.prefab) 和 打包后资源(二进制序列化格式)。
⚠️核心底层真相:所有资源真正数据存于C++引擎内存,C#只拿到对象代理;C# GC回收引用不会自动释放底层资源内存,需要手动管理卸载。
四种主流加载方式
- Resources(内置,不推荐做游戏热更)
- AssetBundle(底层资源包,老方案)
- Addressables(官方新方案,推荐新项目,基于AB封装)
- 直接读取本地文件(File.ReadAllBytes,只适合文本/二进制,不能直接读fbx/prefab)
1. Resources.Load
Resources文件夹是特殊目录,打包时会全部打进主包,自动压缩,不支持热更新。
⚠️打包时Unity会把所有Resources文件夹资源全部打入包内,无法增量更新;资源多会造成包体巨大。
//加载Prefab,路径不带后缀
GameObject prefab = Resources.Load<GameObject>("Prefabs/Player");
GameObject go = Instantiate(prefab);
//加载图片
Texture2D tex = Resources.Load<Texture2D>("Tex/Icon");
- 路径规则:相对于
Resources/,不带文件后缀。 Resources.LoadAll:加载文件夹全部资源。- 卸载:
//卸载没有被任何引用占用的资源
Resources.UnloadUnusedAssets();
//卸载指定资源对象
Resources.UnloadAsset(tex);
缺点:
- 全部打进安装包,无法热更新。
- 不能做增量更新,无法单独更新某一个资源。
- 无法控制资源依赖关系,容易重复打包。>
适用场景:少量固定内置资源,调试。正式游戏不要大量用Resources。
2. AssetBundle(AB包)
把资源打包成独立二进制包,可以放在本地或者CDN服务器,支持热更新。
- 可以拆分多个Bundle,管理依赖,一个资源被多个包引用只存一份。
- 底层,版本、缓存、CRC校验、依赖全部自己手写。
//1.加载AB包文件
AssetBundle ab = AssetBundle.LoadFromFile("Assets/AB/test.ab");
//2.从包加载资源
GameObject prefab = ab.LoadAsset<GameObject>("Player.prefab");
Instantiate(prefab);
//3.卸载AB包
ab.Unload(false); //false:只卸载AB包内存,已加载出来资源保留
ab.Unload(true); //true:卸载包+所有从这个包加载出来的资源对象
关键坑:依赖Bundle
如果A包资源依赖B包里的贴图,必须先加载B包,再加载A包,否则贴图丢失变成粉色。原生AB需要自己维护依赖表。
缺点:
- 需要自己写整套版本管理、缓存、下载、哈希校验、依赖管理。
- 容易忘记卸载造成内存泄漏。>
现在新项目优先用Addressables,不直接裸写AssetBundle。
3. Addressables(官方推荐)
建立在AssetBundle之上的高层封装,解决原生AB的痛点。
- 自动处理依赖、缓存、版本对比、增量更新,远程CDN加载。
- 使用
Address字符串标记资源,不关心资源实际存放哪个AB包。 - 支持本地包+远程热更包,也是现在商业项目主流。
//异步加载,推荐用await
async Task LoadObj()
{
//加载预制体
var handle = Addressables.LoadAssetAsync<GameObject>("Player");
await handle.Task;
GameObject prefab = handle.Result;
Instantiate(prefab);
//用完必须释放句柄!!
Addressables.Release(handle);
}
重要:Addressables有引用计数,LoadAssetAsync必须配套Release;忘记Release资源不会释放,内存泄漏。
Addressables的核心优势:
- 不用手动处理AB包依赖。
- 可以检测远程更新,只下载改动资源。
- 编辑器模式可以直接运行,不需要打包AB,开发效率高。
资源热更流程回顾
- 标记资源为Addressable,分组;
- 构建Remote资源包,上传CDN;
- 客户端启动检测Catalog版本,对比哈希;
- 自动下载变更资源,本地缓存。
4. UnityWebRequest
不是资源加载API,是网络请求,用来下载AssetBundle文件,也可以下载图片文本。
//从网络下载AB包
UnityWebRequest req = UnityWebRequestAssetBundle.GetAssetBundle(url);
yield return req.SendWebRequest();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(req);
UnityWebRequest本身不加载prefab,只负责拿到二进制AB数据。
资源加载关键概念
同步加载 vs 异步加载
- 同步Load(Resources.Load / LoadFromFile):主线程立刻读取磁盘,大资源会卡顿游戏。
- 异步加载(Addressables、LoadFromMemoryAsync、UnityWebRequest):磁盘IO放到后台,主线程不卡帧,正式项目尽量全部异步。
❗不要在Update里面做同步加载大资源,会造成明显卡顿。
资源卸载(高频踩坑点)
Unity资源内存分两块:
- C++引擎资源内存(贴图、模型、mesh数据)
- C#托管对象(C#侧代理对象)
C#对象被GC回收 ≠ C++资源内存释放。
Resources.UnloadUnusedAssets():扫描所有资源,没有任何C#引用的资源才会被卸载;是异步操作。AssetBundle.Unload(true):直接销毁这个AB所有加载出来的资源。- Addressables:依靠引用计数,
Release减少计数,计数0才释放底层资源。
经典坑:把贴图赋值给GameObject的材质,然后丢掉C#贴图引用;材质还持有贴图引用,
UnloadUnusedAssets不会卸载贴图。
资源依赖
例如:Prefab引用一张贴图。
- 加载Prefab,引擎会自动顺带加载贴图。
- 卸载Prefab,贴图不会自动卸载,需要UnloadUnusedAssets/Release清理。
各个方案选型
| 方案 | 热更新 | 打包位置 | 依赖管理 | 适用场景 |
|---|---|---|---|---|
| Resources | ❌不支持 | 打进主包 | 自动但不可控 | 少量固定内置资源 |
| 原生AssetBundle | ✅支持 | 独立ab文件 | 需要自己维护依赖表 | 老项目,底层定制 |
| Addressables | ✅支持 | 封装AB包,远程CDN | 引擎自动管理 | 新项目,商业游戏首选 |
常见误区
- ❌
Destroy(gameObject)会自动释放贴图、模型资源
Destroy只销毁GameObject组件(C++场景对象),贴图、Mesh等资源依旧留在内存,需要单独卸载资源。
- ❌C#变量置null,资源内存就释放
仅仅断开C#引用;引擎内部材质、Prefab实例仍然持有资源引用,资源不会释放。
- ❌Addressables不用Release
每一次LoadAssetAsync,必须Release,引用计数不归零内存永远不会释放。
- ❌Resources文件夹可以用来做热更新
Resources全部打进安装包,无法远程更新,只能发APP商店包。
简单最佳实践总结
- 正式游戏,不要大量使用Resources。
- 新项目优先 Addressables 做资源管理,兼顾本地+远程热更新。
- 所有大资源加载全部用异步,避免主线程卡顿。
- 资源用完必须正确卸载,防止内存持续上涨。
- 资源和代码版本要配套,避免DLL逻辑和AB资源版本不匹配报错。
如果你需要,我可以写一份Addressables完整检测更新+加载+释放的示例代码。


0 条回复
还没有留言,来说点什么吧。