Files
zzzisfunnyboy 694f4db9dc 6.30
2025-06-30 00:49:44 +08:00

12 KiB
Raw Permalink Blame History

【三思Unity】深入浅出资源管理之Asset Import Pipeline v2

每个游戏都是由种类繁多的资源构成,例如:网格、材质、纹理、着色器、动画、音频等。导入并管理这些资源文件,是游戏引擎必备的能力。资源管理包含两部分:离线管理和运行时管理。

在 Unity 2019 LTS 中,对应的解决方案分别是 Asset Import Pipeline v2下文简称 AIP v2 和 Addressable Asset System。本文主要讲解 AIP v2 部分, 包含 v2 相对于 v1 的改进之处,和完整的资源导入流程。

关键词Asset Import Pipeline v2Unity AcceleratorAsset RevisionsAsset Database

1. Asset Import Pipeline v2

资源导入是 Unity 中最耗时的工作流程之一,是 AIP v1 的遗留问题。针对其中最耗时的两个阶段——新资源的导入和平台切换AIP v2 分别提供了对应的解决方案。

1.1 新资源的导入

第一次拉取项目时,项目中没有 Library 文件夹,这意味着 Assets 文件夹和 Packages 文件夹中的所有资源都需要经过 AIP 的导入处理,这步非常耗时。相较而言,直接拉取导入结果,耗时就要少几个数量级。

Unity Accelerator

AIP v2 给出的方案是 Unity Accelerator一种基于 Unity Collaborate 的局域网代理和缓存服务。原理是:团队中有一人导入完资源,导入结果被自动缓存到 Accelerator团队里的其他成员在导入相同版本的资源时Unity 首先检测 Accelerator 上是否已经有对应的导入结果,如果有就下载。因为是本地网络,带宽不是问题,所以在资源较多的项目中,下载时间要远少于导入时间。

在 Unity 给出的测试中一个大小100MB、数量12000个的项目资源Untiy 的导入时间为6分钟Accelerator 的导入时间为30秒节省了90%以上的资源导入时间。然而实际项目中12000个资源的空间占用一般要大于100MB因此该测试仅供参考。

Unity Accelerator 基于 Unity Teams Advaned按团队人数收费。目前提供30天免费试用25GB云存储3人团队的价格是60元/月每增加一名团队成员的价格是48元/月。

1.2 平台切换

在 AIP v1 中Library 文件夹中缓存的导入结果以资源的GUID作为文件名。这天然的导致了一个资源只存在一个导入结果因此每次切换平台都需要重新导入所有资源。这步非常耗时在大型项目中甚至需要数个小时。

在 AIP v2 出现前,临时的解决方案是每个平台分别对应一个项目。缺点是要占用多份项目的硬盘空间,以及每个项目需要单独拉取更新。

Asset Revisions

AIP v2 给出的解决方案是不使用资源的GUID作为导入结果的文件名而是使用资源所有依赖的哈希值作为文件名而平台正是资源的依赖之一资源依赖的说明见下文4.3。于是每个资源因为依赖的不同而拥有多个版本的导入结果它们被称为资源的修订版Asset Revisions。因此 Asset Database 中可以同时缓存多个平台的导入结果,当切换至已经有导入结果的平台时,就不需要再经历耗时的导入处理。

AIP v2 同样存在占用硬盘空间大的缺点,但是相较于每个平台分别对应一个项目,占用空间要小很多。对此 AIP v2 也做了优化处理:只保存每个资源仅平台依赖不同的修订版,其他依赖导致的不同修订版,在 Unity 每次重启时会被删除。

2. 源文件 Source File

Source File 是指由 Unity 之外的软件创建并导出的资源文件。常见的有图片png、jpg、psd、音频wav、ogg、mp3、视频avi、wmv、mp4、模型fbx、obj、c4d、blend、字体ttf、otf、ttc、着色器cg、hlsl、文本文件txt、xml、json、csv、代码文件dll、jar、cpp、c、mm、dylib等。

仅仅将 Source File 放入项目文件夹中,并不代表它成为了项目的一部分。只有经过 AIP 的导入处理Soure File 转变成 Asset 才能为项目所用。

导入处理将 Source File 转换成 Unity 能够处理的格式。这些格式往往硬件友好能够让CPU、图形、音频等硬件立即处理。例如Unity 在运行时不会直接使用 .png 格式的图片,而是使用经过导入处理后的某种具体格式的 texture 文件。

Pipeline 确保了导入结果的确定性,即:相同依赖的 Sourece File 经过导入处理,在任意时刻、任意设备得到的 Asset 是相同的。

3. 资源数据库 Asset Database

Asset Database 包含了两层含义。一层指 Source Files 经过 AIP 导入处理后的数据文件集合,即项目 Library 文件夹中的内容,又称为 Asset Cache。Unity 在项目运行和打包时,使用的正是这里面的数据文件。

另一层指 Unity 封装的 AssetDatabase 类。该类提供了对于 AIP 最重要的一个方法 Refresh以及一系列方便安全的项目文件操作方法例如Contains、CreateAsset、CreateFolder、RenameAsset、CopyAsset、MoveAsset、DeleteAsset、LoadAssetAtPath等。

4. 刷新 Asset Database

将 Source File 放入项目 Assets 文件夹中时,或通过 Package Manager 导入 Package 时AIP 检测到新资源,并执行导入处理。这一步的本质是触发了 AssetDatabase 的 Refresh 方法。

以下三种情况会触发 AssetDatabase 的 Refresh 方法:

  1. Unity Editor 重新获得焦点(如果开启了 Auto-Refresh
  2. 点击 Assets > Refresh 菜单。
  3. 在 C# 代码中直接调用 Refresh 方法;或通过调用 CreateAsset、ImportAsset 等方法,间接触发 Refresh 方法。

AssetDatabase 的 Refresh 方法将按顺序执行以下步骤:

  1. 寻找变更的 Assets并更新 source Asset DatabaseLibrary 文件夹中)
  2. 导入并编译代码相关文件
  3. 重载脚本域
  4. 导入非代码文件
  5. 热重载
  6. 更新 Artifact DatabaseLibrary 文件夹中)

接下来将对上述步骤做较详细介绍,其中某些步骤会被拆分为几个小步骤。

4.1 寻找变更的 Assets

当触发了 AssetDatabase.Refresh() 方法后Unity 开始扫描项目中 Assets 文件夹和 Packages 文件夹中的每个资源。与上次扫描相比,如果资源出现了新增、变更或删除,则将它添加到一个资源变更列表中。

4.2 更新 source Asset Database

计算资源变更列表中的每个资源的哈希值并且根据它们的GUID更新 source Asset Database并从列表中删除对应资源。

4.3 依赖追踪Dependency tracking

资源的依赖Dependencies是指所有能够影响导入结果的数据。资源的 Source File 是最原始的依赖,资源的导入设置、目标平台等也都是依赖。

资源变更的本质,是资源依赖的变更。AIP 追踪了每个资源的所有依赖。资源的任意依赖发生变更,都将引起资源的重新导入,生成新的导入结果。

资源变更的结果,是不同版本的资源缓存。AIP 维护了项目所有资源的导入结果,这些导入结果缓存在 Library 文件夹中又叫做资源缓存Asset Cache。同一个资源的不同导入结果是不同版本的资源缓存又称为资源的修订版Asset Revisions

4.4 导入并编译代码相关文件

Unity 从资源变更列表中收集与代码相关的文件递送给脚本编译管线script compilation pipeline。然后编译器根据脚本文件和 assembly definition 文件生成程序集Assemblies

4.5 重载脚本域Reload the domain

域重载Domain Reloading处理会完全重置脚本的状态包括重置所有静态字段和静态事件句柄。任意脚本的变更都会重载脚本域。为了确保自定义导入器Scripted Importer起作用这步将重启 AssetDatabase.Refresh() 方法。

4.6 导入非代码资源

AIP 基于资源文件名的扩展名识别文件类型再基于文件类型使用对应的导入器Asset Importer处理资源。例如TextureImporter 负责导入 jpg、png 和 psd 等图片资源。导入器分为两种原生导入器Native Importers和自定义导入器Scripted Importers

4.6.1 原生导入器Native Importers

Unity 内置了很多原生导入器,支持大多数资源类型,例如:图片、音频、视频、模型、材质、着色器、字体、文本文件、代码文件等。

4.6.2 自定义导入器Scripted Importers

对于新的资源类型,我们可以编写自己的导入器。对于 Unity 已经提供了导入器的资源类型,也可以使用自定义导入器覆盖原生的导入处理。

部分 Unity 内置的导入器也属于自定义导入器因此这些导入器的处理发生在自定义导入器的处理阶段。例如StyleSheetImporteruss 文件、UIElementsViewImporteruxml 文件)。

4.7 预处理Preprocess和后处理Postprocess

在导入阶段,有许多回调方法,可用于资源导入的预处理和后处理。

预处理方法有:

  • OnpreprocessAsset
  • OnpreprocessAnimation
  • OnpreprocessAudio
  • OnpreprocessModel
  • OnpreprocessSpeedTree
  • OnpreprocessTexture

后处理方法有:

  • OnAssignMaterialModel
  • OnPostprocessAnimation
  • OnPostprocessAssetbundleNameChanged
  • OnPostprocessAudio
  • OnPostprocessCubemap
  • OnPostprocessGameobjectWithAnimatedUserProties
  • OnPostprocessWithUserProperties
  • OnPostprocessMaterial
  • OnPostprocessMeshHierarchy
  • OnPostprocessModel
  • OnPostprocessSpeedTree
  • OnPostprocessSprites
  • OnPostprocessTexture
  • OnPostprocessAllAssets

4.8 重启 AssetDatabase 的 Refresh 处理

在上文中提到,重载脚本域将重启 AssetDatabase.Refresh() 方法。除此以外,还有其他情况会重启 AssetData base 的 Refresh 处理。例如:

  • 如果某个资源导入失败
  • 如果资源在 Refresh 中的导入阶段被更改
  • 如果资源在导入时生成了其他资源
  • 如果在预处理/后处理回调方法中强制重新导入某个文件,例如:在 OnPostProcessAllAssets 方法中使用 AssetDatabase.ForceReserializeAssets 方法或 AssetImport.SaveAndReimport 方法。注意避免无限重导入。
  • 如果在编译完脚本后,某个程序集需要重新加载。
  • 如果以“Text only”保存某个资源但是该资源必须被序列化为二进制格式就会触发 Refresh 处理。例如:带有地形的场景必须被序列化为二进制格式,因为文本格式太笨重。

4.9 热重载 Hot reloading

热重载是指在 Editor 不重启的情况下,导入并应用脚本和资源的变更。热重载在 Editor 的 Play Mode 和非 Play Mode 都有可能发生。

当你更改并保存一个脚本文件时Unity 首先保存所有已加载脚本的序列化变量,等脚本加载完成再恢复这些数据。在热重载完成后,所有非序列化的数据都将丢失。

4.10 更新 Artifact Database

这是 Refrech 的最后一步Library 文件夹中的 Artifact Database 被更新,导入结果被保存在硬盘上。

引用参考

[1] The new Asset Import Pipeline: Solid foundation for speeding up asset imports
[2] The Asset Database
[3] Refreshing the Asset Database