Unity 资源热更概念地图:Catalog、Manifest、Bundle、Package 与 DLC 到底指什么

Unity 开发高级/资深 热更新 资源管理
Unity 资源热更概念地图:Catalog、Manifest、Bundle、Package 与 DLC 到底指什么

资源热更这块最大的沟通成本不在实现,而在名词。同一个词在不同框架里指不同文件,同一个文件在不同框架里叫不同名字:Addressables 叫 Catalog,YooAsset 里没有 Catalog 这个类却有一份 BuildinCatalog;Manifest 既可能是框架的运行时清单,也可能是 Unity 构建过程产出的一个副产物;而 DLC 干脆不是技术名词,是发行名词。

这篇文章不按框架 API 组织,而是按名词组织:每个词先给一句话定义、再给它在文件系统里的落点、最后给"怎么验证它"。目标是下次看到别人文档里的一个词,能立刻判断它说的是哪一层,需要打开哪个文件。

目录

章节 内容
先分清三套版本号 App 版本、资源版本、代码版本,混在一起就永远对不上
Package:资源包 它不是安装包;划分边界决定了后面所有能力的上限
Catalog:清单目录 三种语境下同名不同物,以及它的两种粒度
Manifest:清单字段逐项 AssetList / BundleList 每个字段的用途与坑
Bundle:物理容器 BundleName 与 FileName 的区别,依赖是双向的
DLC:可下载内容 为什么它不是技术概念,以及工程上怎么落地
PlayMode 与资源的三层位置 内置 / 沙盒 / 远端,以及各自的清理语义
代码热更的名词 热更 DLL 与 AOT 补充元数据,两个 DLL 分工完全不同
速查词表 中英对照总表,可直接当交接文档用
怎么查:实操路径 定位资源在哪个包、确认运行时用的哪个版本
常见误解 一整张"以为是 A 其实是 B"的对照表
落地清单 接入新框架时要确认的问题

先分清三套版本号

所有资源热更的排障,第一步都是"你到底在比哪个版本号"。这三套版本号的生命周期、比较方、更新代价都不一样。

版本 典型字段 / 载体 谁负责比较 更新手段 需要重新出包吗
App 版本 原生 versionName / versionCode、强更配置 启动时服务端下发 应用商店覆盖安装
资源版本 PackageVersionres_version.txt 里的整数 客户端与远端清单比对 资源热更
代码版本 热更程序集文件(如 hlwd.game.bytes 随资源版本一起走 资源热更

三条必须记住的结论:

  1. 代码热更不是独立通道。 热更 DLL 就是被打进 Bundle 的一个资源,它走的是和贴图、预制体完全相同的版本比对和下载流程。所以"代码版本"实际是"资源版本"的一个子集,不要把它当第四套版本号去设计流程。
  2. 资源版本和 App 版本不是包含关系,是两条平行线。 一个 App 版本可以对应任意多个资源版本;但资源版本不能向下兼容到比它更老的 App 版本,除非你主动做了降级逻辑。
  3. 比较动作只有"相等 / 不等",不要设计成大小比较。 资源版本用来寻址 CDN 目录(换版本换目录),不是用来判断"用户是不是落后了"。想做灰度、回滚,就换一份清单指向,而不是去比数字大小。

为什么先讲这个:后面所有名词——Package、Catalog、Manifest、Bundle——都存在"每个版本一份"的情况。分不清版本号,就会拿 A 版本的清单去解释 B 版本的报错。

Package:资源包

一句话定义:一组资源的集合,拥有独立的清单、独立的版本号、独立的下载器与独立的缓存目录。

最容易搞混的一点:Package 不是安装包。 安装包叫 APK / AAB / IPA,项目里通常叫"包体";Package 是工程内部对资源的逻辑切分,一个安装包里可以有多个 Package。

一个 Package 到底"独立"了什么

维度 是否独立 说明
清单文件 每个 Package 一套,互不影响
版本号 可以单独涨版本,其他包不动
下载任务 可以有各自的并发与超时策略
缓存目录 可以单独清理、单独校验
引用计数 跨包引用需要显式依赖声明,不会自动共享
CDN 目录 通常按 v{版本}/{包名}/ 分层

划分边界的常见口径

切分维度 适用场景 代价
主资源 / 非主资源 控制首包体积 首启需要一次完整清单比对
语言 多语言发行,按系统语言只下次需要的 文案常被其他资源引用,容易跨包
活动 / 赛季 活动内容独立上下线、独立回滚 活动数量多时清单数量爆炸
DLC / 付费内容 按需购买后下载 需要处理授权与卸载
清晰度 / 画质档 高端机下高清资源 同一资源存在多份,跨档不能复用
平台 桌面与移动格式不通用(如 BC 与 ASTC) 这是硬约束,基本必须分

划分边界是架构决策,不是"顺手分个组"。 一旦按某个维度切了 Package,跨越这个边界的资源引用就会变成"跨包依赖"——它会带来加载时序、卸载粒度、清单复杂度三方面的额外成本。新项目建议先只切"必须切的"(平台、首包/非首包),把 DLC 这类业务维度留到有明确需求时再加。

Catalog:清单目录

这是本文最容易读错的一个词,因为三种语境下都叫 Catalog,但不是同一个文件

语境 名字 实际文件 回答什么问题
Addressables Content Catalog catalog.jsoncatalog.hashcatalog_{时间戳}.json 有哪些资源、地址是什么、在哪个 bundle、依赖谁
YooAsset 没有 Catalog 类,由两个文件分担 BuildinCatalog.json{Package}_{版本}.json 内置带了哪些 bundle;资源到 bundle 的映射
Unity 原生 AssetBundleManifest OutputCache.manifest(构建副产物) bundle 之间的依赖关系

通用语义:catalog 是"目录",而且是两级的

抛开框架,catalog 这个词的本义是目录。而在资源框架里它有两个粒度,两者常被混为一谈:

  1. 清单的清单(对应 BuildinCatalog):包里内置了哪些 Bundle、当前有哪些清单版本可用。它是"找清单"的入口。
  2. 资源的目录(对应 Manifest / Addressables Catalog 的主体):某个资源地址 → 它属于哪个 Bundle → 它依赖哪些 Bundle。它是"找资源"的入口。

所以当有人说"catalog 不对",你要追问的是:是入口清单不对,还是资源映射不对? 这两个问题的排查路径完全不同。

一份真实的内置清单结构

{
  "FileVersion": "1.0.0",
  "PackageName": "DefaultPackage",
  "PackageVersion": "12",
  "Wrappers": [
    {
      "BundleGUID": "531607a7cd2af3b8a31565ec3f2d374c",
      "FileName": "defaultpackage_assets_gamemain_scenes_531607a7cd2af3b8a31565ec3f2d374c.bundle"
    }
  ]
}

读这份文件的正确姿势是倒着读:先看 PackageVersion(当前内置到第几个版本),再看 Wrappers 里每个 FileName 是否真的存在于磁盘上。出现"清单说有这么个包,但磁盘上没有"的时候,问题就锁定在内置产物不完整,而不是下载逻辑。

hash 文件的作用

清单旁边通常有一个同名 .hash 文件(或清单里带 Hash 字段)。它的唯一用途是:让客户端在不下载整个清单的情况下,判断清单有没有变。

  • 只下 hash(极小的文件)→ 和本地比对 → 相同就跳过清单下载 → 直接进下载阶段。
  • 不同才下完整清单。

这就是"每次启动都要检查更新,但流量消耗很小"的实现基础。反过来说,如果 hash 的生成逻辑和清单内容不匹配(例如清单被重新序列化但 hash 没重算),会出现永远认为需要更新的死循环。

必须记住的坑:BundleID 是数组下标

清单里代表 Bundle 的字段在很多框架里是整数 ID,它不是稳定标识符,而是同一份清单内 BundleList 数组的下标

# 给定一个 bundleID,反查是哪个 bundle
$manifest = Get-Content .\DefaultPackage_12.json -Raw | ConvertFrom-Json
$bundleId = 15
$bundle   = $manifest.BundleList[$bundleId]
$bundle.BundleName

由此推出两条结论:

  • 这个 ID 一旦清单重建就会变,不能持久化、不能跨版本比较、不能写死在配置表里。
  • 排查时要拿同一份清单内部的 ID 去查,不要拿 A 版本的报错 ID 去 B 版本清单里找,那样必然对不上。
  • 反查"这个资源依赖了谁"的正确方式是读 DependBundleIDs(一组下标),再逐个映射回 BundleList。人眼读 JSON 很痛苦,优先用构建报告(见下节)。

Manifest:清单字段逐项

Manifest 是"资源目录"的本体。不同框架字段名不同,但结构高度一致:一张资源表 + 一张容器表,用 ID 互相引用。

以 YooAsset 的 PackageManifest 为例:

AssetList(资源表)

字段 含义 注意事项
Address 逻辑地址,业务层加载时用 默认用资源路径;配了可寻址规则后可能变成短名
AssetPath 工程内路径 与 Address 同时存在时,需明确哪个是"对外契约"
BundleID 所属 Bundle 是下标,见上一节
DependBundleIDs 依赖的 Bundle 集合 一组下标;共享资源会在这里指向 share 包
Tags 标签 用于按标签批量下载、预加载

BundleList(容器表)

字段 含义 注意事项
BundleName 逻辑名 相对稳定,会体现打包目录结构
FileName 物理名 BundleName_HashName.bundle,含内容 Hash
FileHash 内容 Hash 判断"这个包的内容变没变"的唯一依据
FileCRC 校验值 下载完整性校验
FileSize 字节数 用于下载进度与体积预算
DependBundleIDs 依赖的其他 Bundle 加载一个包会先递归加载它

读法建议:别读 JSON,读构建报告

原始 JSON 适合程序读,不适合人读。构建过程通常会额外产出一份可读报告,包含四个部分:

报告区块 内容 什么时候看
Summary 整体统计:资源数、包数、总大小 发现包体异常膨胀时
AssetInfos 每个资源:路径、所属包、依赖包 查"这个资源被谁引用"
BundleInfos 每个包:逻辑名、物理名、依赖包、被谁依赖、包含哪些资源 查共享包、查环形依赖
IndependAssets 没有任何包引用、也没被引用的独立资源 查无用资源、查漏加入收集器的资源

其中 BundleInfos 里的 ReferenceBundles(被谁依赖)是最有价值的一列。原始 JSON 只存了"我依赖谁"(正向),要回答"谁依赖我"必须遍历全表构建反向索引——报告已经帮你算好了。

判断一个共享包是否健康,就看它的 ReferenceBundles 数量:只有 1 个引用者的"共享包",说明拆包规则切错了,它白白多了一次 IO 和一份清单开销。

Bundle:物理容器

Bundle 是磁盘上真实存在的文件,前面所有概念最终都要落到它身上。

两个名字,别混用

名字 形态 稳定性 用途
BundleName defaultpackage_assets_gamemain_materials 相对稳定 日志、依赖分析、人工排查
FileName defaultpackage_assets_gamemain_materials_abc123....bundle 内容一变就变 磁盘文件名、CDN 路径、缓存 key

为什么要带 Hash 后缀:因为要支持"同一路径下新旧内容共存"。如果文件名固定,CDN 缓存会给你旧文件;CM 部署新版本时也无法灰度。带 Hash 后,abc123def456 是两个不同 URL,缓存互不干扰,代价是旧文件需要清理策略

依赖是双向的

清单里存的是正向依赖(我依赖谁),但排查时经常需要反向信息(谁依赖我):

  • 共享资源的判定:一个材质被 10 个预制体引用 → 抽成 share 包 → 这 10 个包都会在 DependBundleIDs 里指向它。
  • 卸载安全:卸载 A 包前要确认没有其他已加载包依赖它,否则会出现"资源凭空消失"。
  • 环形依赖:A 依赖 B、B 又依赖 A,加载时死锁。这类问题在正向清单里很难看出来,看报告的反向列表反而一目了然。

命名规则会泄漏架构

BundleName 通常由"打包目录 + 资源类型"拼出来,例如:

defaultpackage_share_assets_gamemain_materials
^^^^^^^^^^^^^ ^^^^^ ^^^^^ ^^^^^^^^^^^^^^
   包名前缀    标记   目录路径      资源类型

所以看一个项目的 BundleName 分布,基本就能推断出它的打包策略:出现大量 share 前缀说明启用了共享包规则;目录层级很深说明按目录打包;同名资源出现在多个包名里说明收集器配置有重叠。

DLC:可下载内容

DLC(Downloadable Content)是发行与商业概念,不是技术概念。

这句话是理解 DLC 的关键。在资源框架里你找不到任何叫 DLC 的 API——因为 DLC 描述的是"这个东西在产品上怎么卖、怎么交付",而技术侧只提供"按需下载一组资源"这个能力。把 DLC 当技术名词去找,就会一直在文档里找不到答案。

DLC 与相邻概念的边界

概念 属于哪个维度 回答的问题 典型关注点
DLC 发行 / 商业 主包之外,用户按需购买或获取的内容单元是什么 定价、授权、上下架、评级
热更(Patch) 交付机制 已发布的内容怎么替换成新版本 版本比对、差异下载、回滚
分包(Split) 包体结构 首包放什么、其余按什么边界拆出去 首包体积、下载时机
Package 技术实现单元 实现上述目标时,资源按什么边界分组成独立清单 清单数量、跨包依赖

一句话概括四者关系:DLC 是一类需求,分包是实现它的包体结构策略,Package 是落地时的技术单元,热更是让它们能被持续更新的机制。

技术侧怎么落地一个 DLC

既然没有 API,落地方式就是组合既有能力,通常需要同时满足下面几条:

  1. 独立 Package:DLC 内容单独一个 Package(或一组),独立清单、独立版本号——这样才能"用户没买就不下载"。
  2. 独立下载入口:不进入默认启动下载流程,只在用户触发时(购买成功、点了某个入口)才开始下载。
  3. 两道门:可达 + 授权。技术上"资源下载完了"和"用户有权访问"是两件独立的事。很多线上事故是资源已经能加载,但授权状态没同步,于是未购买用户通过其他入口意外加载到了内容。不要用"资源没下载"来当权限校验。
  4. 卸载与回收:DLC 是第一个真正需要"用户主动卸载"的场景,要能清理清单与缓存,且不影响主包。
  5. 回滚与下架:DLC 下架时,已经买过的用户要能继续用(点对点服务),没买过的不能再看到入口。这要求版本清单支持"指向旧清单"。
  6. 依赖交叉:DLC 与主包互相引用时,谁先加载?主包更新后 DLC 的清单是否过期?这是 DLC 方案里最容易出问题的一环。

平台差异(同一套技术,不同的约束)

平台 主要约束 对 DLC 的影响
Android(AAB) 有分片机制,但资源分片与资源配置是两套东西 原生分片不能替代资源 DLC 方案
iOS 审核对"下载后可改变功能"的内容敏感 内容形态与文案要提前对齐审核口径
PC / 主机商店 有正式的 DLC SKU 与授权体系(Entitlement) 商业侧的 DLC 与技术侧的资源包要建立映射表
微信小游戏等 有明确的分包数量与体积上限 “DLC” 实际是分包能力的重新组合,不能照搬移动端结论

结论:DLC 方案 = 发行策略 + 包体结构 + 资源框架能力三者的组合。 三方任何一方没对齐,都会出现"技术做完了但商业侧不理解"或"商业卖完了但技术下不下去"的情况。

PlayMode 与资源的三层位置

同一份资源在运行时可能来自三个地方,PlayMode 决定它们的优先级和组合方式

模式 资源来源 典型用途 关键差异
Editor 模拟模式 直接读 AssetDatabase 日常开发 不走 Bundle,加载行为与真机不同
离线模式 只读内置(StreamingAssets) 单机、无网、首包自包含产品 完全不请求远端,也无法热更
联机模式(HostPlay) 内置 + 沙盒缓存 + 远端 绝大多数热更产品 三层按优先级查找,沙盒覆盖内置
Web 模式 远端 HTTP 直读 小游戏、WebGL 无本地缓存概念,依赖浏览器缓存

三层位置与清理语义

位置 物理路径 内容 谁会清它
内置 StreamingAssets 随包发布的清单与 Bundle 只能靠覆盖安装更新
沙盒 persistentDataPath 热更下载下来的清单与 Bundle、版本记录 应用自身策略;覆盖安装/重装会清
远端 CDN,通常 v{版本}/{包名}/ 权威清单与 Bundle 由 CM 部署流程控制

这里面有两个必须理解的行为:

  1. 沙盒的生效方式是把内置清单"复制"过来再更新。 首启时如果沙盒为空,框架会把内置清单复制进沙盒,然后在沙盒那份上继续做增量更新。这解释了为什么首启离线也能跑——它读的是内置产物,而不是远端。
  2. 覆盖安装会清沙盒。 用户覆盖安装新 App 版本后,沙盒里的清单与 Bundle 可能被清掉,于是回到"用内置清单 + 重新下载"。如果新 App 版本内置的是老清单,用户就会先回退到老版本资源,再更新上去——这段窗口期是线上 bug 的高发期,尤其是"覆盖安装后必须重启一次才正常"这类问题。

版本记录文件

热更流程通常会在沙盒里留一个极小的版本文件(例如一行整数),记录"上次成功更新到哪个资源版本"。

  • 它的作用是下次启动的目标基线,避免每次都从头比对清单。
  • 它必须在下载全部成功之后才写入。写早了会出现"版本号是新的,资源还是旧的"这种最难查的状态:清单说资源都在,实际加载的是旧内容。
  • 它和远端不一致时,不要直接报错退出,而是重新走一次完整比对,这是最常见的自愈路径。

代码热更的名词

代码热更会额外引入几个词,其中最容易被混为一谈的是:热更 DLLAOT 补充元数据。两者都是 .bytes 文件、都放在资源里、都由同一份清单管理,但职责完全不同。

名词 是什么 数量 何时需要更新
热更程序集 被编译成热更 DLL 的业务代码(Hotfix 层) 通常 1 个 业务逻辑改动时
AOT 补充元数据 AOT 程序集在热更代码里被泛型实例化时需要的元数据 一组 AOT 层改动、或热更代码新用了 AOT 泛型时
link.xml 告诉裁剪器"这些类型/方法不要裁掉" 1 个 反射、序列化、泛型被裁剪导致运行时才报错时
AOT 泛型引用收集产物 构建时扫出来的"热更代码实际用到的 AOT 泛型组合" 1 个 与 AOT 补充元数据配套

关键推论:

  • 只要热更代码新用到一个 AOT 泛型组合,就必须更新补充元数据。 这是"代码热更上去后,只有某个功能崩溃,报 ExecutionEngineException / Attempting to call method ... for which no ahead of time code was provided“这类问题的根因。
  • AOT 层改动无法热更。 它只存在于 App 版本里。所以架构上要把"AOT 尽可能薄"当作硬约束——AOT 越厚,需要强更的改动就越多。
  • 加载顺序必须是先补元数据、后加载热更程序集。顺序反了会在加载阶段就失败。

速查词表

名词 英文 / 常见写法 一句话定义 常见落点
资源包 Package 一组资源的集合,独立清单 + 独立版本号 框架名称空间下的 Package 对象
清单目录 Catalog “目录"的总称;两级粒度见正文 catalog.json / BuildinCatalog.json
内置清单 BuildinCatalog 包里内置的 Bundle 与清单版本索引 StreamingAssets
资源清单 Manifest / PackageManifest 资源地址到 Bundle 的映射表 {Package}_{版本}.json
摘要文件 .hash 用来快速判断清单是否变化 清单同目录
资源 Asset 最小的加载单位 工程内资源路径
地址 Address / Location 业务层加载时用的逻辑名 清单的 Address 字段
容器 Bundle / AssetBundle 磁盘上的物理包文件 *.bundle
逻辑名 BundleName 相对稳定的包名 清单的 BundleName 字段
物理名 FileName 带内容 Hash 的真实文件名 CDN 路径、本地缓存
共享包 Share Bundle 被多个包共同依赖的包 BundleName 含 share 标识
正向依赖 DependBundles 我依赖谁 清单的 DependBundleIDs
反向依赖 ReferenceBundles 谁依赖我(需构建反向索引) 构建报告的 BundleInfos
可下载内容 DLC 主包之外按需获取的内容单元(发行概念) 无 API,用独立 Package 落地
热更 Patch / HotUpdate 把已发布内容替换为新版本的机制 版本比对 + 下载
分包 Split 首包与其余内容的包体划分策略 收集器与打包规则
首包 Buildin / Base 随安装包一起发出的内容 StreamingAssets
沙盒 persistentDataPath / Sandbox 热更内容的本地落地目录 可写目录
播发模式 PlayMode 决定资源来源组合的运行模式 框架初始化参数
内容校验 CRC / Hash 下载完整性与内容变更判定 清单字段
构建报告 Report 人可读的构建产物说明 *.report
强更 Force Update 必须覆盖安装才能继续使用 App 版本号比对
授权 Entitlement 用户是否有权访问某内容 服务端 / 商店侧

怎么查:实操路径

名词认清了,接下来是"遇到问题怎么定位”。这一节给的是从现象到文件的路径。

想知道"某个资源在哪个包里”

  1. 打开构建报告,在 AssetInfos 里搜资源路径 → 拿到所属 BundleName
  2. BundleInfos 里查这个 BundleName → 看它依赖谁、被谁依赖、包含哪些资源。
  3. 只有拿不到报告时,才回到原始清单里做下标映射。

想知道"这个包为什么这么大"

  1. 看报告 Summary 的体积分布,先确定是"包多"还是"单包大"。
  2. 单包大 → 在 BundleInfos 里看它的 BundleContents 是否有意外的大资源(图集、音频、视频)。
  3. 包多 → 检查收集器规则是否有重叠;IndependAssets 是否有本该合并的资源。

想知道"运行时实际用的是哪个版本"

查什么 怎么查
当前资源版本 读沙盒里的版本记录文件;或框架的 GetPackageVersion()
清单到底加载的是哪一份 对比内置清单与沙盒清单的版本号
资源是从内置还是远端来的 看缓存命中日志;或临时禁用远端,观察是否还能加载
Bundle 的真实内容 用二进制工具列出 Bundle 内的资源名清单,与报告对照

一个通用原则

所有资源问题的证据链都必须落在"文件 + 版本"上。 只看日志会出现"日志说下载成功了,但加载的是旧资源"这种无法收敛的状态;必须同时确认哪一份清单、哪一个版本、哪个物理文件。这也是前面反复强调"分不清版本号就永远对不上"的原因。

常见误解

以为是 A 实际是 B 后果
Catalog 就是 Manifest Catalog 是"目录"的总称,Manifest 是其中"资源映射"这一级 排查时找错文件
Manifest 只有一个 每个 Package、每个版本各一份 拿错版本的清单去解释报错
BundleID 是稳定 ID 它是同一份清单内数组的下标 持久化后跨版本必然失效
DLC 是技术名词 它是发行概念,技术侧只有"按需下载一组资源" 在框架文档里找不到答案
DLC 下载完了就等于有权访问 可达性与授权是两道独立的门 权限绕过,未购买用户加载到内容
热更 DLL 和 AOT 补元数据是一回事 分工完全不同,后者只在特定条件下才需要更新 热更后局部功能崩溃
代码热更有独立通道 热更 DLL 本身就是资源,走同一条链路 设计出多余的第二套版本体系
Bundle 压缩能省内存 容器压缩省磁盘与下载,不省运行时内存 内存优化方向选错
覆盖安装后一切从头开始 沙盒被清 → 先回退到内置清单再更新 “覆盖安装后必须重启一次"类问题
版本号可以比大小 它用于寻址目录,只该做相等判断 灰度与回滚逻辑写歪

落地清单

接入或接手一套资源热更框架时,把下面这些问题问清楚,基本就能对齐所有名词:

  • 这个项目的资源版本号存在哪个文件?谁负责写入?写入时机是在下载前还是下载后?
  • 一共划分了哪些 Package?切分维度是什么?有没有跨包引用?
  • Catalog 的入口文件是哪一个?它和资源映射清单分别叫什么名字?
  • 清单是否带 hash 摘要?更新检查是先比摘要还是直接下清单?
  • BundleID 是下标还是稳定标识?项目里有没有把它当稳定 ID 用错的地方?
  • 有没有可读的构建报告ReferenceBundles 是否用于审查共享包?
  • 是否存在只有一个引用者的共享包?(拆包规则切错的信号)
  • DLC / 活动内容是用独立 Package 落的,还是混在主包里用标签控制?
  • DLC 的授权校验在哪里做?有没有"资源可达即视为有权"的隐患?
  • 三个资源位置(内置 / 沙盒 / 远端)的清理策略分别是什么?覆盖安装会怎样?
  • 热更 DLL 与 AOT 补充元数据是否都进了清单?更新条件是否明确?
  • 有没有回滚路径?回滚时是换清单还是换版本号?

一句话总结

资源热更的名词可以归到四层上:Package 是边界,Catalog / Manifest 是地图,Bundle 是实物,DLC 是需求。

  • Catalog 和 Manifest 不是同义词:Catalog 是"目录”,Manifest 是目录里"资源映射"那一级;框架不同名字还不一样,先确认对方说的是哪一层再讨论。
  • BundleID 是下标不是 ID,所有"跨版本对不上"的问题多半源于此。
  • DLC 不是技术概念,技术侧只有"按需下载一组资源";它真正难的地方在授权、卸载、回滚、依赖交叉,而不是下载。
  • 三套版本号必须分开看,代码热更不是第四套通道,它本身就是资源。
  • 所有结论都要落到文件 + 版本 + 物理路径三件事上,只凭日志无法收敛。

相关阅读:Unity 开发高级/资深 03:资源管理与热更新Unity 图片资源格式与内存Unity 开发高级/资深 技能总览

评论