Unity资源加载


Unity资源分两类:源资源(编辑器工程目录下,.png/.fbx/.prefab) 和 打包后资源(二进制序列化格式)。

⚠️核心底层真相:所有资源真正数据存于C++引擎内存,C#只拿到对象代理;C# GC回收引用不会自动释放底层资源内存,需要手动管理卸载。

四种主流加载方式

  1. Resources(内置,不推荐做游戏热更)
  2. AssetBundle(底层资源包,老方案)
  3. Addressables(官方新方案,推荐新项目,基于AB封装)
  4. 直接读取本地文件(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);

缺点:

  1. 全部打进安装包,无法热更新。
  2. 不能做增量更新,无法单独更新某一个资源。
  3. 无法控制资源依赖关系,容易重复打包。>

适用场景:少量固定内置资源,调试。正式游戏不要大量用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的核心优势:

  1. 不用手动处理AB包依赖。
  2. 可以检测远程更新,只下载改动资源。
  3. 编辑器模式可以直接运行,不需要打包AB,开发效率高。

资源热更流程回顾

  1. 标记资源为Addressable,分组;
  2. 构建Remote资源包,上传CDN;
  3. 客户端启动检测Catalog版本,对比哈希;
  4. 自动下载变更资源,本地缓存。

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资源内存分两块:

  1. C++引擎资源内存(贴图、模型、mesh数据)
  2. C#托管对象(C#侧代理对象)

C#对象被GC回收 ≠ C++资源内存释放。

  1. Resources.UnloadUnusedAssets():扫描所有资源,没有任何C#引用的资源才会被卸载;是异步操作。
  2. AssetBundle.Unload(true):直接销毁这个AB所有加载出来的资源。
  3. Addressables:依靠引用计数,Release减少计数,计数0才释放底层资源。

经典坑:把贴图赋值给GameObject的材质,然后丢掉C#贴图引用;材质还持有贴图引用,UnloadUnusedAssets不会卸载贴图。

资源依赖

例如:Prefab引用一张贴图。

  • 加载Prefab,引擎会自动顺带加载贴图。
  • 卸载Prefab,贴图不会自动卸载,需要UnloadUnusedAssets/Release清理。

各个方案选型

方案 热更新 打包位置 依赖管理 适用场景
Resources ❌不支持 打进主包 自动但不可控 少量固定内置资源
原生AssetBundle ✅支持 独立ab文件 需要自己维护依赖表 老项目,底层定制
Addressables ✅支持 封装AB包,远程CDN 引擎自动管理 新项目,商业游戏首选

常见误区

  1. ❌Destroy(gameObject)会自动释放贴图、模型资源

Destroy只销毁GameObject组件(C++场景对象),贴图、Mesh等资源依旧留在内存,需要单独卸载资源。

  1. ❌C#变量置null,资源内存就释放

仅仅断开C#引用;引擎内部材质、Prefab实例仍然持有资源引用,资源不会释放。

  1. ❌Addressables不用Release

每一次LoadAssetAsync,必须Release,引用计数不归零内存永远不会释放。

  1. ❌Resources文件夹可以用来做热更新

Resources全部打进安装包,无法远程更新,只能发APP商店包。

简单最佳实践总结

  1. 正式游戏,不要大量使用Resources。
  2. 新项目优先 Addressables 做资源管理,兼顾本地+远程热更新。
  3. 所有大资源加载全部用异步,避免主线程卡顿。
  4. 资源用完必须正确卸载,防止内存持续上涨。
  5. 资源和代码版本要配套,避免DLL逻辑和AB资源版本不匹配报错。

如果你需要,我可以写一份Addressables完整检测更新+加载+释放的示例代码。


21 8 月, 2026Garfield God学习笔记阅读 1 次

0 条回复

还没有留言,来说点什么吧。

留言