6.AssetBundle基础总结

  1. 6.总结
    1. 6.2 核心要点速览
      1. AssetBundle 是什么
      2. 相对 Resources,为什么要用 AB
      3. Resources 加载与 AB 加载的常见对比
      4. 编辑器里怎么打 AB:Browser 与资源归包
        1. Build 页常用开关
        2. Configure / Inspect
      5. 磁盘上多出来的文件分别是什么
      6. 运行时加载与卸载要点
      7. 拆分 AB 之后为什么会丢材质
      8. 主包、Manifest 与自动跟依赖
      9. AssetBundleManager 封装思路
      10. 小结记忆
    2. 6.3 面试题精选
      1. 基础题
        1. 1. AssetBundle 一般指什么样的东西?里面通常装什么、不装什么?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. Build 时 LZMA 和 LZ4 差别在哪,为什么更倾向 LZ4?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. 为什么只加载了 Prefab 所在 AB,实例化后会出现材质丢失?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      2. 进阶题
        1. 1. 和 Resources 相比,用 AssetBundle 管资源主要赢在哪几条?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. 同一个 AssetBundle 连续 LoadFromFile 两次会发生什么,工程上怎么防?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. 主包里的 AssetBundleManifest 一般怎么用,GetAllDependencies 之后还要做什么?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      3. 深度题
        1. 1. 「减小包体」和「热更新」用 AssetBundle 时,各自在工程上大致怎么用?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. AssetBundle.Unload 和 UnloadAllAssetBundles 的布尔参数到底控制什么?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. AssetBundleManager 里为什么要「先 LoadDependencies 再 LoadTarget」,字典和主包各管什么?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章

6.总结


6.2 核心要点速览

AssetBundle 是什么

  • 可以把它理解成按目标平台打出来的、带压缩的「资源包」:里面装的是美术与数据向的内容,不是「再塞一份工程源文件」那种概念。
  • 常见会打进包里的东西:模型、贴图、Prefab、音效、材质球等;托管的 C# 程序集逻辑不当作 AssetBundle 里的「资源条目」来理解,原文把「C# 代码」划在 AB 资产讲解范围之外。

相对 Resources,为什么要用 AB

  • 资源组织:比起把什么都丢进 Resources 那条管线,AB 更符合「按需拆分、按包发布」的思路,版本和资源边界更好管。
  • 包体与首包:能打压缩包、拆分内容,有助于控制初次安装体积;Resources 往往倾向整进包、全搜得到,体积与裁剪策略都不如 AB 灵活。
  • 热更新链条:热更语境下常见组合是:运行时拉取 AB 包(以及业务里常见的 Lua 等脚本载体)做资源或逻辑的增补替换;只动内置 Resources 很难支撑「上线后持续换资源」的节奏。

Resources 加载与 AB 加载的常见对比

对比点 Resources AssetBundle
API Resources.Load 一类接口 AssetBundle 装载、再 LoadAsset 等流程
放哪 资源必须在工程的 Resources 目录规则下 理论上路径很自由;原文举例常用 Application.streamingAssetsPathApplication.persistentDataPath
内容形态 更偏「工程里就位的一堆单资源」 更像是打好的资源集合 / 包,一次构建对应一批资源的二进制结果

编辑器里怎么打 AB:Browser 与资源归包

  • 工具:早年可自研编辑器扩展打包,现在多用 Asset Bundle Browser;低版本常见从 Package Manager 装,高版本往往列表里不再有,可走 Addressables 路线,或从 GitHub 导入 AssetBundles-Browser 工程(导入报错时删掉包内 Samples 示例 往往就好),也可以在 Packages/manifest.json 里手写 com.unity.assetbundlebrowser 版本让编辑器去拉包。
  • 资源怎么进包:在 Inspector 底部给资源指定 包名 / 变体,示例包名如 modelicon;可多选一批图塞进同一个包。列表没刷新时点 Browser 上的 Refresh
  • 和脚本的边界不能把 C# 当 AB「内容」塞进去(编译型托管代码);Prefab 本质是序列化数据,会记下挂了哪些脚本,打进包的是 Prefab 数据,脚本程序集仍按平常方式进包,别理解成「把 .cs 打进 AB」。

Build 页常用开关

作用 原文口径
Build Target 打哪一个平台 Windows / iOS / Android 等;换平台要重打
Output Path AB 输出目录 与清空、拷贝等选项联动
Clear Folders 构建前是否清空输出目录 Force Rebuild 不同:后者重打已存在的包,不会顺带删掉「已经不打的悬空包」
Copy To StreamingAssets 是否把结果复制到 StreamingAssets 与运行时 LoadFromFile(Application.streamingAssetsPath + …) 的路径习惯一致
Compression 不压缩 / LZMA / LZ4 LZMA 体最小但要取包内单项时往往得整包解压LZ4 体积略大但按需解压,内存友好,原文倾向 LZ4
Exclude Type Information 包里不写类型信息 不建议勾,否则类型元数据缺失易埋雷
Ignore Type Tree Changes 增量构建时忽略类型树变化 不建议勾
Append Hash 包名后附哈希 便于版本区分、CDN 缓存策略
Strict Mode 任一步报错则整体失败 建议开,早暴露问题
Dry Run 干跑 / 预演式构建 先跑校验、少踩坑;是否完全不落盘以工具版本为准,按团队流程选用

构建若勾了清空目录 + 拷贝 StreamingAssets,Unity 可能弹窗问是否删除目标目录与 StreamingAssets 里旧文件,按流程点确认即可。打完后若在 Project 窗口里没看到 StreamingAssets 更新,右键 Refresh 一下。

Configure / Inspect

  • Configure:看工程里每个 AB 塞了哪些资源。
  • Inspect:把已打好的 AB 文件或目录丢进去,读内部信息(验包、对依赖很有用)。

磁盘上多出来的文件分别是什么

  • 输出目录名若叫 PC,会多出一个与目录同名、看起来像「没亲手建过的包」PC:原文把它当主包 / 总控——放了关键索引、依赖关系等;真正业务向的 modelicon 才是你按包名打出去的内容。
  • 无扩展名:一般是 AB 数据本体;**.manifest:记录资源信息、依赖、版本等元数据的旁路清单**,和同名数据文件成对出现。

运行时加载与卸载要点

  • 路径:示例用 Application.streamingAssetsPath + "/" + bundleName,前提是构建时拷进过 StreamingAssets,或你自己把包部署到别的可读路径再给绝对/相对路径。
  • 同一路径同一 AB 不要 LoadFromFile 两次:会直接抛错(原文引了英文报错);工程上常用 字典缓存已打开的 AssetBundle 引用,并配套 卸载策略,练习题也是这个方向。
  • 从包里掏资源LoadAsset 只写字符串容易撞上同名不同类型泛型重载 C# 里好用,但 Lua 绑定往往吃不到泛型;示例里对 Cube 用 LoadAsset("Cube", typeof(GameObject)) as GameObject 兼顾可读与脚本侧。
  • 异步LoadFromFileAsyncyield returnAssetBundleCreateRequest,再用 assetBundle.LoadAssetAsync(name, type)yield returnAssetBundleRequest,最后从 asset 强转Sprite 等。协程里 Type 别传错,否则后面 as 全废。
  • 卸载Unload(false) / UnloadAllAssetBundles(false) 在示例里是「卸包壳、先留着场景里已经实例化出来的对象」;Update 里演示 UnloadAllAssetBundles(true) 是另一类语义对照,记住 bool 管不管已经 Instantiate 出来的那份,别背反。

拆分 AB 之后为什么会丢材质

  • 根因:Prefab 在 model 里,材质却进了 icon 时,运行期只 LoadFromFile("model")InstantiateGPU 能画 mesh,但材质那条 Asset 还没进内存,表现就是洋红 / 丢材质。
  • 手工修法:再对依赖包执行 LoadFromFile("icon"),不用改 LoadAsset 名字,只要依赖 AB 也在内存里,引用就能对上。
  • 分包里怎么记依赖:材质没在手选包名里也会进别的包时,Browser 里依赖边会自己画出来;把材质硬塞进 icon 且与 model 无自动依赖边时,就不会帮你「连带」进 model 包——依赖长什么样纯看打包划分

主包、Manifest 与自动跟依赖

  • 主包仍是那只与输出文件夹同名的包(如 PC)。先 LoadFromFile(… + 主包名),再用固定资源名 AssetBundleManifest 取出清单对象: main.LoadAsset<AssetBundleManifest>("AssetBundleManifest")
  • GetAllDependencies("model"):返回 model 依赖的其它包名(示例里会打出 icon)。返回值里不含 “model” 自己,所以循环里只加载数组中的包还不够,**务必再对目标包 LoadFromFile("model")**,才能 LoadAsset("Cube", …)
  • 加载顺序:主包 → 读清单 → 按数组把依赖包逐个打开 → 打开目标业务包 → 再取资源。把这段封装成函数后,依赖顺序由清单驱动,不必在业务代码里写死包名先后

AssetBundleManager 封装思路

干什么
主包 + AssetBundleManifest 单独字段缓存;首次用依赖时再 LoadFromFileManifest 从主包取固定名资源。
Dictionary<string, AssetBundle> 防重复 LoadFromFile;键是打包时的 AB 名字符串,和 GetAllDependencies 返回值同一套。
LoadAssetBundleDependencies 先确保主包与清单,再 GetAllDependencies字典里没有才 Load 并加入
LoadAssetBundleTarget 打开请求的那只业务包,同样走字典去重。
LoadAssetBundleTargetAndDependencies 先依赖、后目标,再从字典里拿目标包做 LoadAsset / LoadAssetAsync
LoadAssetBundleResource 系列 同步三条重载 + 异步协程 + UnityAction;**GameObject 自动 Instantiate**,其它 Object 子类直接返回加载结果。
UnloadAssetBundle / ClearAssetBundle 单包 Unload(false)Remove;全清时 UnloadAllAssetBundles(false),字典清空,主包与 Manifest 引用也置空,下次重新拉主包。

主包进不进字典:原文实现里主包单独挂在字段上,依赖与业务包走字典;卸载全清时三类状态一起归零,避免 Manifest 悬在旧主包里。

小结记忆

  • AB = 平台相关、可压缩的资源包;记清「资源侧能打什么」和「托管代码走别的热更链路」,别把所有热更都理解成「往 AB 里塞 C#」。
  • 选型口诀:要强管理、可拆包、可热更、可控首包——往 AB 思路靠;只放永不更新的小内置资源才偶尔考虑 Resources。
  • 打包侧Clear FoldersForce Rebuild 语义别混;压缩默认往 LZ4 想;输出目录里同名主包多承担索引与依赖,无扩展名文件与 .manifest 成对。
  • 加载侧LoadFromFile 缓存防双开;LoadAsset 注意 Lua 不吃泛型时的 typeof + asUnload(bool) 的 true/false 直接决定实例化物体要不要跟包一起带走,对照示例里 false 与空格 true 两段。
  • 依赖链:材质进别的包就只加载 Prefab 会照样丢材质;Manifest 给出依赖名数组后,依赖包 + 目标包都要进内存;GetAllDependencies 不包含目标包自己
  • 管理器:字典键与 AB 名一致;TargetAndDependencies = 先扫依赖再打开目标;主包与清单单独字段,和字典里的业务包分仓。

6.3 面试题精选

基础题

1. AssetBundle 一般指什么样的东西?里面通常装什么、不装什么?

题目

用一两句话说明 AssetBundle 在 Unity 里是什么形态的内容,并说明常见包含与不包含哪些东西。

深入解析
  • 原文把它定义成特定目标平台的、经过压缩的资源归档,类比「压缩包」是为了形状上别和「普通文件夹资源」搞混。
  • 会装进去的:模型、贴图、Prefab、音频、材质等数据与资源对象
  • 刻意排除在「AB 里装的资产」叙述之外的C# 托管代码——练习题里把「资源、Lua 脚本打包成 AB 做热更」和「减少包体」并列为作用,回答时别说成「把 .cs 当普通资源塞进 AB 就代表脚本热更完成了」,概念上要分清资源包和脚本方案的分工。
答题示例

AssetBundle 是打出来的、跟平台相关的压缩资源包,用来装模型贴图 Prefab 音频材质这类内容。

它面向的是资源侧资产,不把 C# 程序集当成往里塞的那类「资源条目」;逻辑更新要另说一套方案。

参考文章
  • 1.AB包的基本概念和作用

2. Build 时 LZMA 和 LZ4 差别在哪,为什么更倾向 LZ4?

题目

结合打包选项,说明两种压缩在「取包内单个资源」时的典型行为差异。

深入解析
  • LZMA压缩率最好,但原文强调要摸到包里某一个资源,往往得先把整个 AB 解压开来,时间和内存尖峰都难看。
  • LZ4:体积比 LZMA 稍大一点,却是 「用哪个资源就解压哪块」 的风格,内存压力小,和热更新、边玩边下场景更合拍,所以原文把它标成建议默认优先考虑那一档。
  • 不压缩:加载最快、包最大,线上一般不会当首选
答题示例

LZMA 最省磁盘,但取单个资源经常意味着整包先解压,代价大。

LZ4 牺牲一点体积换按需解压,内存和行为更可控,所以原文更推荐日常使用。

参考文章
  • 2.生成AB包资源文件

3. 为什么只加载了 Prefab 所在 AB,实例化后会出现材质丢失?

题目

结合分包规则,说明丢材质的典型原因和最小修复手段。

深入解析
  • Prefab 与材质不在同一 AB:引用关系仍在序列化数据里,但材质所在 AB 没加载时,运行期找不到那张 Material,shader / 贴图链断掉,Unity 常用洋红提示。
  • 修复:对承载材质的依赖 ABLoadFromFile,或改用下一条 Manifest 自动批量加载依赖;不是改 Instantiate 参数能解决的。
答题示例

Prefab 在 model,材质在 icon,只开 model 时材质资源没进内存,Instantiate 出来就丢材质。

把 icon 也 Load 进来,或按 Manifest 把依赖包先扫一遍,再实例化。

参考文章
  • 4.AB包的依赖包

进阶题

1. 和 Resources 相比,用 AssetBundle 管资源主要赢在哪几条?

题目

目录约束、API、内容组织、包体与更新里挑重点,对比 Resources 与 AssetBundle。

深入解析
  • 目录:Resources 必须把资源放进 Resources;AB 生成的包路径约束弱,部署到 StreamingAssets、持久化目录或 CDN 都可,便于模拟真实发版。
  • API:两套加载入口完全不同,别背混。
  • 组织:练习题写得很直白——AB 是打好的资源集合,Resources 更像工程内就位的零散资源;拆包、增量、依赖关系都建立在「包」这一层。
  • 包体与热更:AB 可压缩、可拆分,利于减小首包;热更时换包或增量拉资源,比动内置 Resources 现实得多。
答题示例

Resources 只能从 Resources 目录 Load,API 固定;AB 是打出来的包,路径灵活,常用 StreamingAssets 或 persistentDataPath。

组织上 AB 是一坨打好的资源集合,Resources 多是工程里的单资源;包体上可以打压缩、拆分,线上资源迭代和热更也是 AB 那套更顺。

参考文章
  • 1.AB包的基本概念和作用

2. 同一个 AssetBundle 连续 LoadFromFile 两次会发生什么,工程上怎么防?

题目

说明报错现象,并给出与练习题一致的管理思路。

深入解析
  • Unity 侧会把同文件的 AB 视为已在内存里占坑,再 LoadFromFile 一次会直接报错,原文贴了英文提示大意:同名 AB 已加载
  • 防法没有魔法:维护「包名 → 已加载 AssetBundle」映射,二次请求直接返回缓存引用;一旦引入缓存,就必须有对称的 Unload / 版本切换清理,否则会泄漏或拉到脏数据。
答题示例

第二次 LoadFromFile 会直接抛异常,提示同名 AB 已加载。

用字典记已经打开的句柄,二次加载走缓存;同时要配卸载钩子,热更换包时才知道什么时候真正 Unload。

参考文章
  • 3.使用AB包资源文件

3. 主包里的 AssetBundleManifest 一般怎么用,GetAllDependencies 之后还要做什么?

题目

按原文步骤说明:加载主包、取清单、拉依赖、再取业务资源。

深入解析
  • 主包名与输出目录名一致;LoadAsset<AssetBundleManifest>("AssetBundleManifest") 资源名固定。
  • GetAllDependencies(业务包名) 得到依赖包名字符串数组,循环 LoadFromFile 只解决「别人」;业务包自身要再 LoadFromFile(业务包名) 才能对该包 LoadAsset
  • 这样写可以戒掉手写依赖顺序,和 Browser 里看到的依赖边一致。
答题示例

先 Load 主包,取出 Manifest,对目标包名调 GetAllDependencies,把返回里的每个依赖包 Load 一遍。

数组里不含目标包自己,所以要再 Load model,然后才能从 model 里 LoadAsset 出 Cube。

参考文章
  • 4.AB包的依赖包

深度题

1. 「减小包体」和「热更新」用 AssetBundle 时,各自在工程上大致怎么用?

题目

结合项目经验或上文结论,说明 AB 如何帮助控制安装体积,以及如何接入资源热更(脚本侧点到即可)。

深入解析
  • 包体:打 AB 时选择压缩、把非首包内容拆出去,首装包只带最小集合;Resources 往往大量内置,裁剪空间小。
  • 热更:运行时从指定路径或网络拿到新的或增量的 AB,加载后与本地版本协同;原文练习题把脚本热更和 Lua 等脚本打包成 AB 放在同一句话里,答题时承认「逻辑与资源两条线」即可,除非面试官追问到具体框架。
答题示例

包体侧通过打 AB、开压缩、把可后载内容拆包,首装只带必须的;资源热更是上线后拉 AB 替换或增补。

脚本热更在工程里常与 Lua 等载体配套;别把所有热更都说成「往 AB 里塞 C#」。

参考文章
  • 1.AB包的基本概念和作用

2. AssetBundle.Unload 和 UnloadAllAssetBundles 的布尔参数到底控制什么?

题目

解释 falsetrue包里资源和场景实例的影响,并说明示例里为何同时演示两种写法。

深入解析
  • 参数语义在原文里被口述成:是否连「场景里已经加载/实例化出来的那份对象」也一起卸掉
  • false 路线:更多是在释放 AB 压缩块与内部表Instantiate 的物体还活着,适合短暂换手还没想好要不要删实例的阶段;代价是托管对象与 AB 的 bookkeeping 容易不同步,后续若还要从同包再加载要记得引用别悬空。
  • true 路线从该 AB 出来的实例也干掉,更狠、更干净,适合玩家切大关、整包资源作废一类场景,但要防止 UI、池化对象还在用旧引用。
  • 文末示例在 StartUnload(false)Update 里空格键触发 UnloadAllAssetBundles(true),就是用对照实验帮助你记 bool 两端的差别。
答题示例

bool 决定卸包时是否把从该 AB 加载出来并在场景里的实例也一起干掉。

false 更像「先关压缩档案但物体暂留」,true 是连根拔起;示例里 Start 用 false、空格用 true 就是在对比这两种力度。

参考文章
  • 3.使用AB包资源文件

3. AssetBundleManager 里为什么要「先 LoadDependencies 再 LoadTarget」,字典和主包各管什么?

题目

对照原文封装,说明加载顺序、字典键含义、主包与清单为何单独存放。

深入解析
  • 顺序:依赖 AB 若晚于业务包加载,实例化时照样缺资源;所以 API 写成 GetAllDependencies 扫一遍并 LoadFromFile,再打开目标包,和资源实际引用方向一致。
  • 字典:缓存 **已打开的 AssetBundle**,键用 打包时的包名字符串;重复请求同包不会二次 LoadFromFile,与「同一 AB 文件不要重复 LoadFromFile」的约束配套。
  • 主包 + Manifest:单独字段保存;清单对象不从字典取,Clear 时和字典一并清空,避免主包句柄还留着、Manifest 却指向过期构建。
答题示例

先加载依赖再打开目标包,否则 Prefab 引用到的材质包可能还没进内存。

字典按包名缓存 AssetBundle;主包挂字段里专门喂 Manifest,全清时字典和主包指针一起扔掉,免得混版本。

参考文章
  • 4.AB包的依赖包
  • 5.AB包管理器


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏