当前位置: 首页 > article >正文

Unity Adressables 使用说明(三)构建内容(Build Content)

Build Content

【概述】Build Content

内容构建会处理 Addressables 组,生成内容目录(content catalog)、运行时设置以及包含你的资源的 AssetBundles。Addressables 使用这些文件在运行时加载内容。

你可以配置 Addressables 系统将 Addressables 内容作为每个 Player 构建的一部分进行构建,也可以在制作 Player 构建之前单独构建内容。有关配置这些选项的详细信息,请参阅 Building Addressables content with Player builds。

配置构建

如果配置 Unity 将内容作为 Player 构建的一部分进行构建,请使用 Unity 编辑器构建设置窗口中的 Build 或 Build and Run 按钮启动构建。你也可以在命令行中调用编辑器,传递-buildPlatformPlayer选项之一,或者使用如 BuildPipeline.BuildPlayer 的 API 启动构建。在所有情况下,Unity 都会在构建 Player 之前作为预构建步骤构建 Addressables 内容。

如果配置 Unity 单独构建内容,必须使用 Addressables 组窗口中的 Build 菜单启动 Addressables 构建,如 Making builds 中所述。下次为项目构建 Player 时,它会使用当前平台最后一次 Addressables 内容构建运行所生成的工件。有关自动化 Addressables 构建过程的信息,请参阅 Build scripting。

内容构建类型

你的内容构建可以生成两类内容:

  • 本地内容(Local content):直接包含在 Player 构建中的内容。只要使用本地内容的默认构建路径,Addressables 系统会自动管理本地内容。如果更改本地构建路径,必须在制作 Player 构建之前将工件从本地构建路径复制到项目的 Assets/StreamingAssets 文件夹中。
  • 远程内容(Remote content):应用安装后从 URL 下载的内容。你有责任将远程内容上传到托管服务器,以便应用可以在RemoteLoadPath指定的 URL 处访问这些内容。

有关内容构建生成的文件的更多信息,请参阅 Build artifacts。

Group 和 Profiles

项目的组设置(group settings)决定了组所属的类别。活跃配置文件(active Profile)确定 Addressables 系统用于构建和加载内容的具体路径和 URL。Addressable Asset settings 还包含影响内容构建的选项,例如是否完全构建远程内容。

启动构建

可以通过脚本或组窗口启动构建。有关如何扩展 Addressables 内容构建的信息,请参阅 Build scripting。

Addressables 系统包括以下构建脚本:

  • Default Build Script:基于组、配置文件和 Addressables 系统设置执行完整内容构建。
  • Update a Previous Build:执行差异内容构建以更新先前创建的构建。
  • Play Mode Scripts:Play 模式脚本是一个构建脚本,控制 Editor 在 Play 模式下如何访问内容。有关详细信息,请参阅 Play Mode Scripts。

构建脚本还提供了一个功能来清除它们创建的缓存文件。可以从 Groups window 的 Build > Clean Build 菜单运行这些功能。

构建包含 Addressables 内容的 Player

在开发过程中修改 Addressable 资源时,必须在构建 Player 之前重新构建 Addressables 内容。可以在构建 Player 之前作为单独的步骤运行 Addressables 内容构建,也可以将 Addressables 内容构建与 Player 构建一起运行。

将 Addressables 内容与 Player 一起构建会很方便,但会增加构建时间,特别是在大型项目中。这是因为即使没有修改任何资源,Unity 也会重新构建 Addressables 内容。如果在大多数构建之间没有更改 Addressables 内容,请考虑禁用此选项。

项目 Addressable 资源设置中的 Build Addressables on Player Build 设置指定用于构建 Addressables 内容的选项。可以为每个项目选择适当的选项,或遵循全局偏好设置。当设置项目级别设置时,它适用于构建项目的所有贡献者。偏好设置适用于未设置特定值的所有 Unity 项目。

构建命令(Build commands)

从组窗口顶部工具栏的 Build 菜单访问构建命令(菜单:Window > Asset Management > Addressables > Groups)。

菜单提供以下选项:

  • New Build:选择一个构建脚本来运行完整内容构建。Addressables 包括一个构建脚本,默认构建脚本。如果创建了自定义构建脚本,可以在这里访问它们。有关更多信息,请参阅 Build scripting 。
  • Update a Previous Build:基于先前的构建运行差异更新。当支持远程内容分发并发布更新内容时,更新构建可以生成较小的下载文件。有关更多信息,请参阅 Content Update Builds 。
  • Clean Build:选择一个命令来清理现有的构建缓存文件。每个构建脚本可以提供一个清理功能,可以从此菜单调用。

资源依赖关系(Asset dependencies)概述

在项目的构建设置中包含一个场景并构建 Player 时,Unity 会将该场景和场景中使用的任何资源包含在应用程序的内置数据中。同样,Unity 会将项目中 Resources 文件夹中的任何资源包含在一个单独的内置资源集合中。区别在于,场景中的资源只能作为场景的一部分加载,而 Resources 中的资源可以独立加载。

Addressable 资源可以作为一组额外的本地资源内置到应用程序中,也可以作为托管在服务器上的远程资源构建,当需要时下载。可以独立于应用程序本身更新远程资源,尽管远程资源不能包含代码,因此只能更改资源和序列化数据。
How project assets are exported to a player build

如果在场景和 Resources 文件夹中都使用同一个资源,那么 Unity 在构建时会复制该资源,而不是共享单个实例。例如,如果在内置场景中使用某个材质,同时在位于 Resources 文件夹中的预制件中使用该材质,即使材质资源本身不在 Resources 文件夹中,也会在构建中得到两个该材质的副本。如果将同一个材质标记为 Addressable,则会得到三个副本。项目的 StreamingAssets 文件夹中的文件永远不能被文件夹外的资源引用。

在构建 Player 之前,必须对 Addressable 资源进行内容构建。在 Player 构建过程中,Unity 将本地 Addressables 复制到 StreamingAssets 文件夹中,以便将它们与放置在 StreamingAssets 中的任何资源一起包含在构建中。构建过程结束时,Unity 会删除这些资源。必须将内容构建生成的远程 Addressables 文件上传到托管服务。有关更多信息,请参阅构建。
在项目中使用 Addressables 时,最好将 Resources 文件夹中的任何场景和数据移到 Addressable groups 中并作为 Addressables 进行管理。

构建设置的场景列表必须包含至少一个场景。可以创建一个最小场景来初始化应用程序。

Resources 文件夹中的少量数据通常不会引起性能问题。如果使用将资源放置在 Resources 文件夹中的第三方包,除非它们引起问题,否则不需要移动它们。

引用子对象(sub-objects)

Unity 部分基于资源和脚本之间的引用关系来确定在内容构建中包含哪些内容。子对象引用使这个过程更复杂。

如果一个 AssetReference 指向的是一个 Addressable 资源的子对象,Unity 会在构建时将整个对象构建到 AssetBundle 中。如果 AssetReference 指向一个 Addressable 对象(如 GameObject、ScriptableObject 或 Scene),而这个对象直接引用一个子对象,Unity 只会将子对象作为隐式依赖项构建到 AssetBundle 中。

资源和 AssetBundle 依赖项

将资源添加到 Addressables group 时, content build 时会将该资源打包到一个 AssetBundle 中。这种情况下,该资源被显式包含在包中,称为显式资源。

如果一个资源引用了其他资源,那么被引用的资源就是原始资源的依赖项,称为资源依赖项(asset dependency)。例如,如果资源被打包到 AssetBundle A 中,而被引用的资源被打包到 AssetBundle B 中,那么包 B 就是包 A 的依赖项,这称为 AssetBundle dependency 。有关更多信息,请参阅AssetBundle 依赖项手册页。

资源依赖项的处理方式取决于它们是否也是 Addressable 资源。被标记为 Addressable 的依赖项会根据它们所在组的设置打包到 AssetBundles 中。这可能与引用资源在同一个包中,也可能在不同的包中。未标记为 Addressable 的依赖项会包含在其引用资源的包中。被引用的资源隐式包含在包中,称为隐式资源(implicit asset)

使用 Build Layout Report 工具显示由内容构建生成的 AssetBundles 的详细信息。

引用多个隐式资源

如果多个 Addressable 引用了同一个隐式资源,则每个包含引用 Addressable 的包中都会包含该隐式资源的副本。
非 Addressable 资源会被复制到每个包含引用 Addressable 的包中

当一个隐式资源包含在多个包中时,可能会在运行时实例化该资源的多个实例,而不是游戏逻辑期望的单个实例。如果在运行时更改实例状态,只有同一个包中的对象可以检测到更改,因为所有其他资源现在都有各自的实例,而不是共享同一个实例。

为了防止这种重复,可以将隐式资源标记为 Addressable 资源,并将其包含在现有包之一中或添加到不同的包中。该资源被添加到的包在加载引用它的 Addressables 时会被加载。如果 Addressables 被打包到不同的 AssetBundle 中,那么包含被引用资源的包就是 AssetBundle 依赖项。

在加载当前包中的任何资源时,必须加载依赖的包,而不仅仅是包含引用的资源。尽管不会加载这个 AssetBundle 中的其他任何资源,但加载包本身会有运行时成本。有关更多信息,请参阅加载 AssetBundle 依赖项的内存影响。

创建构建(Create a build)

使用 Addressables 包时,可以将内容(AssetBundles)的构建与应用程序 Player 的构建分离。Addressables 包提供了自己的构建脚本,可从 Groups 窗口的工具栏访问。

可以将 Addressables 内容与 Player 构建一起构建,也可以将它们作为单独的步骤构建。

还可以执行完整构建或更新构建。

创建完整构建(Create a full build)

要创建完整构建:

  1. 配置项目的 group settings 。
  2. 如果要远程分发内容,请配置 Profile 和 Addressables system settings 以启用远程内容分发。
  3. 选择正确的 Profile。
  4. 从 Groups window 启动构建。
设置构建和加载路径(Set up build and load paths)

Profile 定义了本地与远程内容的构建和加载路径的变量。可以创建多个 Profile 以用于不同类型的构建。例如,可以在 Unity 编辑器中开发项目时使用一个 Profile,发布最终内容构建时使用另一个 Profile

对于大多数项目,只有在支持远程内容分发时才需要多个 Profile。在开发过程的不同阶段,通常不需要更改本地路径。大多数项目应将本地内容构建到默认的本地构建路径,并从默认的本地加载路径(解析为 StreamingAssets 文件夹)加载内容。

Windows 文件路径限制

Windows 的文件路径限制为 260 个字符。如果内容的构建路径创建的路径在 Windows 上达到了或超过了此限制,构建将失败。

如果项目位于接近字符限制的目录中,可能会遇到文件路径限制问题。Scriptable Build Pipeline 在构建期间在临时目录中创建 AssetBundles。此临时路径是项目的子目录,可能会生成超过 Windows 限制的字符串。如果 Addressables 内容构建失败并出现“Could not find a part of the path”错误,并且您使用的是 Windows,则这可能是原因。

默认本地路径

本地构建路径默认设置为 Addressables.BuildPath 提供的路径,该路径位于 Unity 项目的 Library 文件夹中。Addressables 根据当前的平台构建目标设置在本地构建路径中附加一个文件夹。构建多个平台时,会将每个平台的构建工件放置在不同的子文件夹中。

同样,本地加载路径默认设置为 Addressables.RuntimePath 提供的路径,解析为 StreamingAssets 文件夹。Addressables 也会在路径中添加平台构建目标。

将本地 bundle 构建到默认构建路径时,构建代码会在构建 Player 时临时将 artifacts 从构建路径复制到 StreamingAssets 文件夹,并在构建结束后将其删除。

默认远程路径

Addressables 将默认远程构建路径设置为任意选择的文件夹名 ServerData,该文件夹在项目文件夹下创建。构建将当前平台目标作为子文件夹添加到路径中,以分离不同平台的唯一 artifact 。

默认远程加载路径为 http://localhost/ 并附加当前 Profile 的 BuildTarget 变量。必须将此路径更改为计划加载 Addressable 资产的 base URL。

使用不同的 profiles 设置远程加载路径,以适应不同类型的开发、测试或发布。例如,可以有一个 Profile 从本地主机服务器加载资产进行常规开发构建,一个 Profile 从测试环境加载资产进行 QA 构建,另一个 Profile 从内容分发网络(CDN)加载资产进行发布构建

设置远程内容构建

要设置远程内容构建:

  1. 导航到 AddressablesSystemSetting 资产(菜单:Window > Asset Management > Addressables > Settings)。
  2. Catalog 下,启用 Build Remote Catalog 选项。CatalogBuildPathLoadPath 设置必须与用于远程组(remote groups)的设置相同。在大多数情况下,使用 RemoteBuildPathRemoteLoadPath Profile 变量。
  3. 对于要作为远程内容构建的每个 Group ,将 BuildPathLoadPath 设置为 RemoteBuildPathRemoteLoadPath Profile 变量(或所需的自定义值)。
  4. 打开 Profiles 窗口(菜单:Window > Asset Management > Addressables > Profiles)。
  5. RemoteLoadPath变量设置为计划托管远程内容的 URL。如果不同类型的构建需要不同的 URL,请为每种构建类型创建一个新的 Profile。有关更多信息,请参阅 Profiles and Hosting。

更多信息请参考 Remote content distribution 。

执行构建

配置 group 和 Addressables 系统设置后,可以运行内容构建:

  1. 打开 Groups 窗口(菜单:Windows > Asset Management > Addressables > Groups)。
  2. 从工具栏上的 Profile 菜单中选择所需的 Profile。
  3. 从 Build > New Build 菜单中选择 Default Build Script。(如果创建了自己的构建脚本,也可以从此菜单访问它们。)

Default Build Script 会为每个组创建一个或多个 AssetBundles,并将它们保存到本地或远程构建路径。

创建更新构建

当分发远程内容时,可以对先前发布的构建进行差异更新,以最小化用户需要下载的数据量(与完整构建相比)。

正确配置远程 groups 并拥有包含远程内容的先前构建后,可以通过以下步骤执行 Content Update Builds :

  1. 打开 Groups 窗口(菜单:Windows > Asset Management > Addressables > Groups)。
  2. 从工具栏的 Profile 菜单中选择所需的 profile。
  3. 从 Build 菜单中选择 Update a Previous Build
  4. 找到要更新的构建生成的 addressables_content_state.bin 文件(默认位置在 Assets/AddressableAssetsData/TargetPlatform 文件夹中)。
  5. 点击 Open 开始更新构建。

为了更新现有客户端,在适当测试后,将更新的远程内容复制到托管服务。(更新构建包括所有本地和远程内容——在 Content Update Builds 后创建的任何 Player 构建都将包含完整的 Addressable 资产集。)

更新先前的构建不会更改addressables_content_state.bin文件。对未来的更新构建使用相同版本的文件(直到发布另一个从New Build菜单创建的完整构建)。

有关如何以及何时使用 Content Update Builds 的信息,请参阅 Content Update Builds 。

最小化对包的更改

内容包可能很大,为了对小更改更新整个包,可能会导致因小改动而更新大量数据。启用 Addressables settings 中的MonoScript Bundle Naming Prefix选项,将构建包含 MonoScript 对象的 AssetBundle,与序列化数据分开。如果序列化类数据没有更改,则只有 MonoScript 包会更改,其他包不需要更新

需要重建 Addressables 的脚本更改

Unity 使用 MonoScript 对象引用 Addressables 内容中的类。此对象使用程序集名称、命名空间和类名或引用类定义类。

在运行时加载内容时,Unity 使用 MonoScript 从 Player 程序集加载并创建运行时类的实例。

MonoScript 的更改在 Player 和已构建的 Addressables 内容之间需要保持一致。必须重建 Player 内容和 Addressables 内容,以正确加载类。

以下操作可能导致 MonoScript 数据的更改:

  • 将脚本文件移动到另一个程序集定义文件下的目录
  • 更改包含类的程序集定义文件名称
  • 添加或更改类的命名空间
  • 更改类名

通过了解这些可能导致更新的情况,可以更好地管理和最小化构建更新带来的影响。

自定义构建脚本(Build Scripting)

你可以使用 Addressables API 自定义项目构建,方法包括:

  • 从脚本启动构建
  • 覆盖(Override)现有脚本
  • 扩展 [BuildScriptBase](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEditor.AddressableAssets.Build.DataBuilders.BuildScriptBase.html) 或实现 [IDataBuilder](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEditor.AddressableAssets.Build.IDataBuilder.html)

当你自定义构建脚本以处理不同类型的资源或以不同的方式处理资源时,可能还需要自定义 Play Mode Scripts,以便 Unity 编辑器在 Play 模式下以相同的方式处理这些资源。

从脚本启动构建

要从另一个脚本启动构建,请调用 AddressableAssetSettings.BuildPlayerContent 方法。

在启动构建之前,设置 active ``[Profile](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/manual/AddressableAssetsProfiles.html)active build script 。你也可以设置不同于默认的 AddressableAssetSettings 对象。

BuildPlayerContent 在执行构建时考虑以下信息:

  • [AddressableAssetSettingsDefaultObject](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.html)
  • [ActivePlayerDataBuilder](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEditor.AddressableAssets.Settings.AddressableAssetSettings.ActivePlayerDataBuilder.html#UnityEditor_AddressableAssets_Settings_AddressableAssetSettings_ActivePlayerDataBuilder)
  • addressables_content_state.bin 文件
设置 AddressableAssetSettings

AddressableAssetSettings 定义的设置包括组列表和要使用的配置文件。

要访问在编辑器中显示的设置(菜单:Window > Asset Management > Addressables > Settings),使用静态 AddressableAssetSettingsDefaultObject.Settings 属性。如果需要,可以在构建中使用不同的设置对象。

要在构建中加载自定义设置对象:

static void getSettingsObject(string settingsAsset)
{
    // 这一步是可选的,你也可以使用默认设置:
    // settings = AddressableAssetSettingsDefaultObject.Settings;

    settings = AssetDatabase.LoadAssetAtPath<ScriptableObject>(settingsAsset) as AddressableAssetSettings;

    if (settings == null)
        Debug.LogError($"{settingsAsset} couldn't be found or isn't a settings object.");
}
设置活跃配置文件(Active Profile)

使用 BuildContent 启动的构建使用活动配置文件的变量设置。要在自定义构建脚本中设置活动配置文件,将所需配置文件的 ID 分配给 AddressableAssetSettingsDefaultObject.Settings 对象的 activeProfileId 字段。

AddressableAssetSettings 对象包含配置文件列表。使用所需配置文件的名称查找其 ID 值,然后将 ID 分配给 activeProfileId 变量:

static void setProfile(string profile)
{
    string profileId = settings.profileSettings.GetProfileId(profile);
    if (String.IsNullOrEmpty(profileId))
        Debug.LogWarning($"Couldn't find a profile named, {profile}, using current profile instead.");
    else
        settings.activeProfileId = profileId;
}
设置活动构建脚本(Active Build Script)

BuildContent 方法根据当前的 ActivePlayerDataBuilder 设置启动构建。要使用特定的构建脚本,将 IDataBuilder 对象在 AddressableAssetSetting.DataBuilders 列表中的索引分配给 ActivePlayerDataBuilderIndex 属性。

构建脚本必须是实现 IDataBuilderScriptableObject,并且必须将其添加到 AddressableAssetSettings 实例的 DataBuilders 列表中。添加到列表后,使用标准的 List.IndexOf 方法获取对象的索引:

static void setBuilder(IDataBuilder builder)
{
    int index = settings.DataBuilders.IndexOf((ScriptableObject)builder);

    if (index > 0)
        settings.ActivePlayerDataBuilderIndex = index;
    else
        Debug.LogWarning($"{builder} must be added to the DataBuilders list before it can be made active. Using last run builder instead.");
}
启动构建

设置要使用的配置文件和构建器后,可以启动构建:

static bool buildAddressableContent()
{
    AddressableAssetSettings.BuildPlayerContent(out AddressablesPlayerBuildResult result);
    bool success = string.IsNullOrEmpty(result.Error);

    if (!success)
    {
        Debug.LogError("Addressables build error encountered: " + result.Error);
    }

    return success;
}

要检查成功与否,使用 BuildPlayerContent(out AddressablesPlayerBuildResult result)result.Error 包含 Addressables 构建失败时返回的任何错误消息。如果 string.IsNullOrEmpty(result.Error) 为真,则构建成功。

启动构建的示例脚本

以下示例将几个菜单命令添加到编辑器中的 Window > Asset Management > Addressables 菜单。第一个命令使用预设的配置文件和构建脚本构建 Addressable 内容。第二个命令构建 Addressable 内容,如果成功,则构建 Player。

如果你的构建脚本进行了需要域重载的设置更改,你应该使用 Unity 命令行选项运行构建脚本,而不是在编辑器中交互运行。有关详细信息,请参阅域重载。

#if UNITY_EDITOR
    using UnityEditor;
    using UnityEditor.AddressableAssets.Build;
    using UnityEditor.AddressableAssets.Settings;
    using System;
    using UnityEngine;

    internal class BuildLauncher
    {
        public static string build_script = "Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset";

        public static string settings_asset = "Assets/AddressableAssetsData/AddressableAssetSettings.asset";

        public static string profile_name = "Default";
        private static AddressableAssetSettings settings;

        static void getSettingsObject(string settingsAsset)
        {
            // 这一步是可选的,你也可以使用默认设置:
            // settings = AddressableAssetSettingsDefaultObject.Settings;

            settings = AssetDatabase.LoadAssetAtPath<ScriptableObject>(settingsAsset) as AddressableAssetSettings;

            if (settings == null)
                Debug.LogError($"{settingsAsset} couldn't be found or isn't a settings object.");
        }

        static void setProfile(string profile)
        {
            string profileId = settings.profileSettings.GetProfileId(profile);
            if (String.IsNullOrEmpty(profileId))
                Debug.LogWarning($"Couldn't find a profile named, {profile}, using current profile instead.");
            else
                settings.activeProfileId = profileId;
        }

        static void setBuilder(IDataBuilder builder)
        {
            int index = settings.DataBuilders.IndexOf((ScriptableObject)builder);

            if (index > 0)
                settings.ActivePlayerDataBuilderIndex = index;
            else
                Debug.LogWarning($"{builder} must be added to the DataBuilders list before it can be made active. Using last run builder instead.");
        }

        static bool buildAddressableContent()
        {
            AddressableAssetSettings.BuildPlayerContent(out AddressablesPlayerBuildResult result);
            bool success = string.IsNullOrEmpty(result.Error);

            if (!success)
            {
                Debug.LogError("Addressables build error encountered: " + result.Error);
            }

            return success;
        }

        [MenuItem("Window/Asset Management/Addressables/Build Addressables only")]
        public static bool BuildAddressables()
        {
            getSettingsObject(settings_asset);
            setProfile(profile_name);
            IDataBuilder builderScript = AssetDatabase.LoadAssetAtPath<ScriptableObject>(build_script) as IDataBuilder;

            if (builderScript == null)
            {
                Debug.LogError(build_script + " couldn't be found or isn't a build script.");
                return false;
            }

            setBuilder(builderScript);

            return buildAddressableContent();
        }

        [MenuItem("Window/Asset Management/Addressables/Build Addressables and Player")]
        public static void BuildAddressablesAndPlayer()
        {
            bool contentBuildSucceeded = BuildAddressables();

            if (contentBuildSucceeded)
            {
                var options = new BuildPlayerOptions();
                BuildPlayerOptions playerSettings = BuildPlayerWindow.DefaultBuildMethods.GetBuildPlayerOptions(options);

                BuildPipeline.BuildPlayer(playerSettings);
            }
        }
    }
#endif
域重载

如果你的脚本化构建过程涉及更改设置并在构建 Addressables 内容之前触发域重载,那么你应该使用 Unity 的命令行参数而不是在编辑器中运行脚本来执行这些构建。这些类型的设置包括:

  • 更改定义的编译器符号
  • 更改平台目标或目标组

当你在编辑器中交互运行脚本(例如使用菜单命令)时,脚本在域重载发生之前完成执行。因此,如果你立即启动 Addressables 构建,代码和导入的资源仍然处于原始状态。必须等待域重载完成后再开始内容构建

最佳实践是在从命令行运行构建时等待域重载完成,因为在交互脚本中很难或不可能可靠地进行。

以下示例脚本定义了两个函数,可以在命令行运行 Unity 时调用。ChangeSettings 示例设置指定的定义符号。BuildContentAndPlayer 函数运行 Addressables 构建和 Player 构建。

#if UNITY_EDITOR
    using System;
    using UnityEditor;
    using UnityEditor.AddressableAssets;
    using UnityEditor.AddressableAssets.Build;
    using UnityEditor.AddressableAssets.Settings;
    using UnityEditor.Build.Reporting;
    using UnityEngine;

    internal class BatchBuild
    {
        public static string build_script = "Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset";

        public static string profile_name = "Default";

        public static void ChangeSettings()
        {
            string defines = "";
            string[] args = Environment.GetCommandLineArgs();

            foreach (var arg in args)
                if (arg.StartsWith("-defines=", StringComparison.CurrentCulture))
                    defines = arg.Substring(("-defines=".Length));

            var buildSettings = EditorUserBuildSettings.selectedBuildTargetGroup;
            PlayerSettings.SetScriptingDefineSymbolsForGroup(buildSettings, defines);
        }

        public static void BuildContentAndPlayer()
        {
            AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings;

            settings.activeProfileId = settings.profileSettings.GetProfileId(profile_name);

            IDataBuilder builder = AssetDatabase.LoadAssetAtPath<ScriptableObject>(build_script) as IDataBuilder;

            settings.ActivePlayerDataBuilderIndex = settings.DataBuilders.IndexOf((ScriptableObject)builder);

            AddressableAssetSettings.BuildPlayerContent(out AddressablesPlayerBuildResult result);

            if (!string.IsNullOrEmpty(result.Error))
                throw new Exception(result.Error);

            BuildReport buildReport = BuildPipeline.BuildPlayer(EditorBuildSettings.scenes, "d:/build/winApp.exe", EditorUserBuildSettings.activeBuildTarget, BuildOptions.None);

            if (buildReport.summary.result != BuildResult.Succeeded)
                throw new Exception(buildReport.summary.ToString());
        }
    }
#endif

要调用这些函数,请在终端或命令提示符或脚本中使用 Unity 的命令行参数:

D:\Unity\2020.3.0f1\Editor\Unity.exe -quit -batchMode -projectPath . -executeMethod BatchBuild.ChangeSettings -defines=FOO;BAR -buildTarget Android
D:\Unity\2020.3.0f1\Editor\Unity.exe -quit -batchMode -projectPath . -executeMethod BatchBuild.BuildContentAndPlayer -buildTarget Android

注意:如果你在命令行参数中指定了平台目标,可以在同一命令中执行 Addressables 构建。但是,如果你想在脚本中更改平台,应该在单独的命令中进行,例如本示例中的 ChangeSettings 函数。

自定义构建脚本

要配置新的自定义脚本,请将其添加到 Build and Play Mode Scripts 列表中。

自定义脚本扩展 [BuildScriptBase](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEditor.AddressableAssets.Build.DataBuilders.BuildScriptBase.html) 类或实现 [IDataBuilder](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEditor.AddressableAssets.Build.IDataBuilder.html) 接口。可以重写多种方法,例如 ClearCachedDataCanBuildData<T>。如果扩展 BuildScriptBase 类,最重要的方法是 BuildDataImplementation<TResult>,这是用于设置或构建内容的方法。

自定义脚本可以是 Build Script 或 Play Mode Script。这取决于 CanBuildData<T> 方法的实现方式。构建脚本只能构建类型为 AddressablesPlayerBuildResult 的数据,因此方法实现如下:

public override bool CanBuildData<T>()
{
    return typeof(T).IsAssignableFrom(typeof(AddressablesPlayerBuildResult));
}

这允许脚本列在 Build 菜单中。

Play Mode Scripts 只能构建类型为 AddressablesPlayModeBuildResult 的数据,因此方法实现如下:

public override bool CanBuildData<T>()
{
    return typeof(T).IsAssignableFrom(typeof(AddressablesPlayModeBuildResult));
}

这允许脚本列在 Play Mode Scripts 菜单中。

有关示例,请参见 Custom Build and Play Mode Scripts Sample 。

扩展默认构建脚本

如果希望使用与默认构建脚本 BuildScriptPackedMode 相同的基本构建,但希望以不同方式处理特定的 groups 或资产类型,可以扩展并重写默认构建脚本。如果构建脚本正在处理的 group 或资产是你想要以不同方式处理的,你可以运行自己的代码。否则,可以调用基类版本的函数以使用默认算法。

有关示例,请参见 Addressable variants project 。

保存内容状态

如果支持 remote content distribution 并在 Player 版本之间更新内容,则必须在构建时记录 Addressables groups 的状态。记录状态可以让你使用 Update a Previous Build 脚本执行差异构建。

有关详细信息,请参见[BuildScriptPackedMode](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEditor.AddressableAssets.Build.DataBuilders.BuildScriptPackedMode.html)[ContentUpdateScript](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEditor.AddressableAssets.Build.ContentUpdateScript.html)的实现。

编译时构建

如果你有一个触发域重载的预构建步骤,那么必须特别注意,确保 Addressables 构建本身在域重载完成后才开始。

使用如[PlayerSettings.SetScriptingDefineSymbolsForGroup](https://docs.unity3d.com/ScriptReference/PlayerSettings.SetScriptingDefineSymbolsForGroup.html)设置脚本定义符号或 [EditorUserBuildSettings.SwitchActiveBuildTarget](https://docs.unity3d.com/ScriptReference/EditorUserBuildSettings.SwitchActiveBuildTarget.html)切换活跃构建目标的方法,会触发脚本重新编译和重载。Unity 编辑器代码的执行会继续当前加载的域,直到域重载并停止执行。任何 platform dependent compilation 或自定义定义符号在域重载完成后才会设置。这可能导致代码依赖这些定义符号正确构建时出现意外问题,并且很容易被忽视。

在构建前更改脚本

要切换平台,或在代码中修改编辑器脚本并继续设置定义符号,则必须执行域重载。在这种情况下,不应使用 -quit 参数,否则编辑器将在调用的方法执行后立即退出。

当域重载时,将调用 InitializeOnLoad。以下代码演示了如何设置脚本定义符号并在编辑器代码中响应这些符号,域重载完成后构建 Addressables。对于切换平台和平台相关编译也可以采用相同的过程。

[InitializeOnLoad]
public class BuildWithScriptingDefinesExample
{
    static BuildWithScriptingDefinesExample()
    {
        bool toBuild = SessionState.GetBool("BuildAddressables", false);
        SessionState.EraseBool("BuildAddressables");
        if (toBuild)
        {
            Debug.Log("域重载完成,按照请求构建 Addressables");
            BuildAddressablesAndRevertDefines();
        }
    }

    [MenuItem("Build/Addressables with script define")]
    public static void BuildTest()
    {
#if !MYDEFINEHERE
        Debug.Log("设置 SessionState 以通知在下次域重载时请求 Addressables 构建");
        SessionState.SetBool("BuildAddressables", true);
        string originalDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
        string newDefines = string.IsNullOrEmpty(originalDefines) ? "MYDEFINEHERE" : originalDefines + ";MYDEFINEHERE";
        Debug.Log("设置脚本定义符号,这将开始编译并启动编辑器脚本的域重载。");
        PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, newDefines);
#endif
    }

    static void BuildAddressablesAndRevertDefines()
    {
#if MYDEFINEHERE
        Debug.Log("为所需构建设置了正确的脚本定义符号");
        AddressableAssetSettings.BuildPlayerContent();
        string originalDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
        if (originalDefines.Contains(";MYDEFINEHERE"))
            originalDefines = originalDefines.Replace(";MYDEFINEHERE", "");
        else
            originalDefines = originalDefines.Replace("MYDEFINEHERE", "");
        PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, originalDefines);
        AssetDatabase.SaveAssets();
#endif
        EditorApplication.Exit(0);
    }
}

构建 Sprite Atlas

一些 SpriteAtlas 的选项可以改变 Unity 加载 sprites 的方式。如果你打算使用Use Asset DatabasePlay Mode Script ,这一点尤其重要。

以下示例说明了 Addressables 如何与其他资源不同地处理SpriteAtlas

Addressable Sprites

在独立组中的 Sprites

你有三个分别在三个独立组中的 Addressable 纹理,每个纹理构建后大约为 500KB。由于它们存在于不同的组中,Unity 将它们构建成三个独立的 AssetBundles。每个 AssetBundle 大约使用 500KB,并且只包含 sprite 纹理和相关的元数据,没有依赖项。

在非 Addressable SpriteAtlas 中的 Sprites

在上一个示例中,将三个纹理放入非 Addressable SpriteAtlas 中。这种情况下,Unity 仍然生成三个 AssetBundles,但它们的大小不同。其中一个 AssetBundle 包含 atlas 纹理并使用大约 1500KB。其他两个 AssetBundles 只包含 sprite 元数据,并将 atlas AssetBundle 列为依赖项。

虽然你无法控制哪个 AssetBundle 包含纹理,但这个过程是确定性的,因此在不同的重建过程中同一个 AssetBundle 始终包含纹理。这是标准依赖项重复的主要区别。sprites 依赖于 SpriteAtlas 纹理加载,而该纹理并未构建到所有三个 AssetBundles 中,而是仅构建到一个中。

在 SpriteAtlas AssetBundle 中的 Sprites

这次,前一个示例中的 SpriteAtlas 在其自身的 AssetBundle 中标记为 Addressable。Unity 现在创建四个 AssetBundles。三个含有 sprites 的 AssetBundles 每个仅几 KB,并依赖于第四个包含 SpriteAtlas 的大约 1500KB 的 AssetBundle。三个 sprite AssetBundles 仍然依赖于 SpriteAtlas AssetBundle。但是,SpriteAtlas AssetBundle 只能包含元数据,纹理可能会在其他 sprite AssetBundles 中。

含有 Sprite 依赖项的 Addressable Prefabs

Sprite Prefabs

你有三个 Addressable sprite prefabs,每个 prefab 依赖于其自身的 sprite(大约 500KB)。分别构建这三个 prefabs 结果是每个大约 500KB 的三个 AssetBundles。

在非 Addressable SpriteAtlas 中的 Sprite Prefabs

将上一个示例中的三个纹理添加到一个 SpriteAtlas 中,并且该 atlas 未标记为 Addressable。在这种情况下,SpriteAtlas 纹理被复制。所有三个 AssetBundles 大约为 1500KB。根据一般的依赖项重复规则,这是预期的,但与前一部分看到的行为相反。

在 SpriteAtlas AssetBundle 中的 Sprite Prefabs

上一个示例中的 SpriteAtlas 现在也标记为 Addressable。符合显式包含的规则,SpriteAtlas 纹理仅包含在包含 SpriteAtlas 的 AssetBundle 中。含有 prefabs 的 AssetBundles 将这个第四个 AssetBundle 列为依赖项。这将导致三个大约 500KB 的 AssetBundles 和一个大约 1500KB 的 AssetBundle。

Build Shaders

默认情况下,Unity 会剔除在任何场景中未使用的着色器变体(strips shaders variants)。这可能会排除仅在 AssetBundles 中使用的变体。为了确保不会剔除某些变体,可以在 Graphics Settings 中的 Shader Stripping 属性中包含它们。

例如,如果你有使用 lightmap 相关着色器(如 Mixed Lights)的 Addressable 资源,请前往Edit > Project Settings > Graphics > Shader Stripping并将Lightmap Mode属性设置为Custom

质量设置(Quality Settings)也会影响 AssetBundles 中使用的着色器变体。

Build Artifacts

Content build 会在多个位置创建文件,Unity 不会将每个文件都包含在 built player 中。通常,Unity 会在built player中包含与本地内容相关的文件,而排除与远程内容相关的文件

大多数与本地内容相关的文件位于 Library/com.unity.addressables 文件夹中。这是Library文件夹中的一个特殊子文件夹,Unity 用来存储 Addressables 文件。有关 Library 文件夹的更多信息,请参考导入资源(Importing assets)。

Artifacts 包含在 player 中

在 player build 期间,Addressables 系统会将以下文件从 Library/com.unity.addressables/aa/<AddressablesPlatform>文件夹复制到[StreamingAssets](https://docs.unity3d.com/6000.0/Documentation/Manual/StreamingAssets.html)文件夹中:

  • Local AssetBundles:这些是.bundle文件,依据你的 group、profile 和平台设置而定。默认情况下,这些文件位于[BuildTarget](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/EditorUserBuildSettings-activeBuildTarget.html)子文件夹中。要更改 group 生成的 bundle 文件的位置,请修改[Build & Load Paths](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/manual/ContentPackingAndLoadingSchema.html)设置。
  • settings.json:包含运行时使用的 Addressables 配置数据。
  • catalog.json:内容目录,用于在没有更新的 Remote Catalog 时定位和加载资源。有关 catalogs 的更多信息,请参考 Content catalogs 。
  • AddressablesLink/link.xml:防止 Unity linker 剔除你的资源使用的类型。有关代码剔除的更多信息,请参考 Managed Code Stripping 。Unity 2021.2 及以后版本中,此文件临时复制到 AddressableAssetSettings.ConfigFolder,或如果没有 settings 文件,则复制到 Assets/Addressables_Temp 文件夹中。

有关平台名称的完整列表,请参考[AddressablesPlatform](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.AddressablesPlatform.html)

不包含在 player 中的 Artifacts

以下 artifacts 不包含在 built player 中。

远程内容(Remote content)

用于远程内容的文件应上传到托管服务器。默认情况下,这些文件位于 ServerData 文件夹中。

这些文件包括:

  • Remote AssetBundles:这些是 .bundle 文件,依据你的 group、profile 和平台设置而定。默认情况下,这些文件位于 [BuildTarget](https://docs.unity3d.com/2023.1/Documentation/ScriptReference/EditorUserBuildSettings-activeBuildTarget.html) 子文件夹中。要更改 group 生成的 bundle 文件的位置,请修改 Build & Load Paths 设置。
  • catalog_{timestamp or player version}.json:远程 catalog ,下载后会覆盖本地 catalog。仅在启用 Content update settings 中的 Build Remote Catalogs 选项时才创建此文件。要更改此文件的位置,请在内容更新设置中修改 Build & Load Paths schema。默认情况下,文件名包含构建时间戳。要使用版本号代替,请在 catalog 设置中指定 Player Version Override 的值。有关 catalogs 的更多信息,请参考 Content catalogs 。
  • catalog_{timestamp or player version}.hash:用于检查自上次客户端应用程序下载以来远程 catalog 是否发生变化。与远程 catalog 文件一样,只有在启用内容更新设置中的 Build Remote Catalogs 选项时才创建此文件。要更改此文件的位置,请在内容更新设置中修改 Build & Load Paths。默认情况下,文件名包含构建时间戳。要使用版本号代替,请在 catalog 设置中指定 Player Version Override 的值。有关 catalogs 的更多信息,请参考 Content catalogs 。
内容状态文件(Content state file)

addressables_content_state.bin 文件用于进行 Content Update Builds 。如果你支持动态内容更新,则每次发布完整内容 build 后必须保存此文件。否则,你可以忽略此文件。

默认情况下,此文件位于 Assets/AddressableAssetsData/<AddressablesPlatform>。有关所有平台名称,请参考 [AddressablesPlatform](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.AddressablesPlatform.html)。要更改此文件的位置,请在 Content update settings 中指定 Content State Build Path 的值。

诊断数据(Diagnostic data)

还可以创建其他文件来收集有关内容 build 的数据。

这些文件包括:

  • Library/com.unity.addressables/AddressablesBuildTEP.json:build 性能数据。有关更多信息,请参考 Build profiling 。
  • Library/com.unity.addressables/buildlayoutreport:有关 build 生成的 AssetBundles 的信息。有关更多信息,请参考 Build layout report 。

Content Catalogs

Content catalogs 是 Addressables 用于根据系统提供的 keys 查找资源物理位置的数据存储。Addressables 为所有可寻址资源构建一个单一的 catalog。当你构建应用程序的 player 时,catalog 会被放置在 StreamingAssets 文件夹中。local catalog 可以访问远程和本地资源,但如果你希望在应用程序的完整构建(full build)之间更新内容,则必须创建一个远程 catalog。

Remote Catalog

远程 catalog 是一个独立的副本,你将其与远程内容一起托管。

最终,Addressables 只使用其中一个 catalog。一个 hash 文件包含 catalog 的 hash(一个数学指纹)。如果构建了远程 catalog ,并且它的 hash 与本地 catalog 不同,那么它将被下载、缓存并取代内置的本地 catalog。当你生成 Content Update Builds 时,hash 会更新,并且新的远程 catalog 指向任何更新资源的更改版本。

虽然 Addressables 为每个项目生成一个 content Catalog ,但你可以加载其他项目创建的 catalogs,以加载这些项目生成的可寻址资源。这允许你使用独立项目来开发和构建一些资源,这可以使大型项目的迭代和团队协作更容易。有关在运行时加载 catalogs 的信息,请参见 Managing catalogs at runtime 。

Catalog Settings

以下是用于 catalogs 的设置:

  • Catalog settings:用于配置本地和远程 catalogs 的选项。
  • Content update settings:仅用于配置远程 catalog 的选项。

为了最小化 catalog 大小,请使用以下设置:

  • Compress the Local Catalog:如果你主要关注构建中 catalog 的大小,可以在 Catalog settings 中找到一个选项叫做 Compress Local Catalog。此选项将游戏随附的 catalog 构建成一个 AssetBundle。压缩 catalog 会使文件本身变小,但请注意,这会增加 catalog 的加载时间。

有几个 group 设置可以帮助减少 catalog 大小,例如 Internal Asset Naming Mode。有关更多信息,请参考 Advanced Group settings 。

Shared AssetBundles

除了从你的 AddressableAssetGroups 生成的 bundles 之外,build 还可以生成一些特殊的 bundles,称为 shared AssetBundles。这些是 unitybuiltinassets AssetBundle 和 MonoScript AssetBundle。

Built-in AssetBundle

Unity 生成的 unitybuiltinassets 包含 player 所需的资源和任何由 build 中包含的资源使用的内置 shaders 。所有引用内置 shader(如标准 Shader)的 Addressable 资源都是通过引用这个特殊的 shader AssetBundle 来实现的。

你可以通过 Addressables Build settings 中的 Built In Bundle Naming Prefix 选项更改内置 shader bundle 的命名方法。

MonoScript AssetBundle

要启用或禁用 MonoScript AssetBundle,请更改 Addressables build 设置中的 MonoScript Bundle Naming Prefix 选项。MonoScript bundle 有以下列出的命名选项,通常用于多项目情境下。它用于将 MonoScript 行为构建到可以作为依赖项引用的 AssetBundles 中。

Shared AssetBundles 的生成选项来自默认的 AddressableAssetGroup。默认情况下,这个 group 名为 Default Local Group (Default),并使用本地 build 和 load 路径。在这种情况下,shared bundles 不能作为内容更新的一部分进行更新,只能在新的 player build 中进行更改。

内容更新限制检查工具无法检测到 bundle 的更改,因为它仅在内容构建期间生成。因此,如果你计划将来对 shared bundles 进行内容更改,请将默认 group 设置为使用远程 build 和 load 路径,并将其 Update Restriction 设置为 Can Change Post Release

Content Update Builds

【概述】 Content Update Builds

当你通过远程分发内容时,可以在不重新构建和重新发布整个应用程序的情况下进行内容更改。当 Addressables 系统在运行时初始化时,它会检查是否有更新的 content Catalog 。如果存在,系统会下载新的 catalog,并在加载资源时,下载所有 AssetBundles 的新版本。

然而,当你用新的 content catalog 重新构建所有内容时,已安装的玩家也必须重新下载所有远程 AssetBundles,无论其中的资源是否更改。如果你有大量内容,则重新下载所有内容可能需要相当多的时间,并可能影响玩家留存率。为了使这个过程更高效,Addressables 包提供了一些工具,可以用来识别更改的资源并生成 Content Update Builds 。

以下图表说明了如何使用 Addressables 工具来生成较小的内容更新,从而只需要玩家下载新的或更改的内容:
The workflow for reducing the size of content updates

Content Update Builds 流程

在发布完整应用程序时,你首先构建 Addressables 内容,然后进行 player 构建。player 构建包含本地 AssetBundles,你需要将远程 AssetBundles 上传到内容分发网络 (CDN) 或其他托管服务。

生成 Addressables 内容构建的默认构建脚本总是会创建 addressables_content_state.bin 文件,该文件是有效发布内容更新所必需的。你必须保存每个平台上发布的每个完整应用程序版本的该文件

在需要用户下载并安装新的 player 构建的完整应用程序版本之间,你可以对项目中的 Addressable 资源进行更改。由于 AssetBundles 不包含代码,请不要在项目中开发 asset 更改的版本中进行代码更改。
本地和远程资源你都可以更改。

内容更新工具

Addressables package 包含一些工具,可以用来减少你远程分发内容的更新大小。

内容更新工具包括:

  • Check for Content Update Restrictions tool:根据 group 设置,为 Content Update Builds 准备你的 group 组织。
  • Update a Previous Build script:执行 Content Update Builds 的构建脚本。

你必须保存 Default Build Script 生成的 addressables_content_state.bin 文件,以便将来更新的每个构建使用。每次运行构建脚本时,该文件都会更新。确保保存发布内容构建生成的版本。有关处理先前内容状态文件(content state file)的相关 Addressable 设置,请参阅 Settings 。

对于有自己补丁系统的平台或不支持远程内容分发的平台,不要使用 Content Update Builds 。每次构建你的游戏都应该是一个完整的全新内容构建。在这种情况下,你可以丢弃或忽略每次为平台生成的 addressables_content_state.bin 文件。
当你想发布内容更新时,手动运行Check Content Update Restrictions工具,或确保检查作为更新构建过程的一部分运行。此检查会检查 addressables_content_state.bin 文件,并根据其所在 group 的设置,将更改的资源移动到一个新的 remote Group 。

构建更新的内容

要构建更新的 AssetBundles,运行Update a Previous Build脚本。此工具还使用 addressables_content_state.bin文件。它会重新构建所有内容,但生成的 catalog 会从其原始 AssetBundles 访问未更改的内容,从新的 AssetBundles 访问更改的内容。

最后一步是将更新的内容上传到你的 CDN 。你可以上传所有生成的新 AssetBundles 或者仅上传那些名称更改的 bundles。未更改的 bundles 使用与原始相同的名称,并会覆盖它们。

你可以按照相同的过程进行额外的内容更新。始终使用原始发布的**addressables_content_state.bin**文件

有关逐步说明,请参阅 Building content updates 。

何时进行完整重构

Addressables 只能分发内容,不能分发代码。因此,代码更改需要新的 player 构建,通常也需要全新的内容构建。虽然新的 player 构建有时可以重用 CDN 上的旧内容,但你必须分析现有 AssetBundles 中的类型树(type tree)是否与你的新代码兼容。

请注意,Addressables 本身是代码,因此更新 Addressables 或 Unity 版本需要创建新的 player 构建和全新的内容构建。

构建内容更新(Update a Previous Build)

要构建内容更新,请运行Update a Previous Build脚本:

  1. 如果你不希望Update a Previous Build自动运行检查,请运行 Check for Content Update Restrictions 工具。
  2. 在 Unity 编辑器中打开 Addressables 组窗口(菜单:Window > Asset Management > Addressables > Groups)。
  3. 在工具栏的构建菜单中运行Update a Previous Build脚本。

构建会生成一个 content Catalog 、一个 hash 文件和 AssetBundles 。

生成的 content Catalog 与原始应用程序构建中的 catalog 同名,覆盖旧的 catalog 和 hash 文件。应用程序在运行时加载 hash 文件,以确定是否有新 catalog 可用。系统从应用程序中随附或已下载的现有 bundles 中加载未修改的资源。

系统使用 addressables_content_state.bin 文件中的内容版本字符串和位置信息来创建 AssetBundles。如果一个 AssetBundle 中没有更新内容,则它会使用与所选更新构建中的文件相同的文件名写入。如果 AssetBundle 包含更新内容,则会生成一个包含更新内容的新 bundle,并具有一个新文件名,以便它可以与原始文件共存于内容托管服务中。只需将具有新文件名的 AssetBundles 复制到托管内容的位置,尽管你可以安全地上传所有内容。

系统还会为不能更改的内容(如任何本地 AssetBundles )构建 AssetBundles ,但你不需要将它们上传到内容托管位置,因为没有 Addressables 资源条目引用它们。

不要在构建新 player 和进行内容更新之间更改构建脚本,如 player 代码或 Addressables 。这可能会导致应用程序行为不可预测。

此外,如果你删除了 Addressables 构建在项目库文件夹中创建的本地内容 bundles,那么当你运行游戏或应用程序并使用“使用现有构建(需要已构建的组)”播放模式脚本时,尝试加载这些 bundles 中的资源将失败。

检查内容更新限制工具(Check for Content Update Restrictions Tool)

“检查内容更新限制”工具会为 Content Update Builds 准备你的 group 组织。该工具检查 addressables_content_state.bin 文件和 group 设置。

如果 group 的[Update Restrictions](https://questerai.feishu.cn/wiki/X1RPwif66iJ9p8k5IpxcXRr9nPe#part-QUlidRtltoNCDfxAwrscr7fvnkh)在先前构建中设置为Prevent Updates,该工具会让你选择是否将任何更改的资源移动到一个新的远程 group。除非有特定原因不这样做,否则应用建议的更改或恢复这些资源的更改。

当你创建更新构建时,新 catalog 将更改的资源映射到它们新的 remote AssetBundles,同时仍将未更改的资源映射到其原始 AssetBundles 。检查内容更新限制不会检查禁用Prevent Updates的 group 。

根据 AssetDatabase.GetAssetDependencyHash 返回的 hash 认为资源已更改。此编辑器 API 有其局限性,可能无法准确反映构建时计算的 AssetBundle 更改。例如,它会计算.cs文件内容的 hash,这意味着在.cs文件中进行的空格更改会导致不同的 hash,但实际包含该文件的 AssetBundle 未更改。有关更多信息,请参阅“需要重建 Addressables 的脚本更改”。

运行工具的方法:

  1. 在 Unity 编辑器中打开 Addressables 组窗口(菜单:Window > Asset Management > Addressables > Groups)。
  2. 在组窗口中,从工具栏的Tools菜单运行Check for Content Update Restrictions
  3. 如果需要,请查看工具所做的 group 更改。你可以更改工具创建的任何新远程 group 的名称,但将资源移动到不同的 groups 可能会产生意想不到的后果。
    在这里插入图片描述

在运行“检查内容更新限制”工具之前,请使用版本控制系统创建一个分支。该工具会以适合更新内容的方式重新排列你的资源 groups。分支操作确保下次发布完整 player 构建时,可以返回到你喜欢的内容安排。

运行时检查内容更新

你可以添加自定义脚本,定期检查是否有新的 Addressables 内容更新。使用以下函数调用开始更新:

public static AsyncOperationHandle<List<string>> CheckForCatalogUpdates(bool autoReleaseHandle = true)

List<string> 包含修改的定位器 ID (modified locator ID)列表。你可以过滤此列表以仅更新特定 ID,或者将其完全传递给 UpdateCatalogs API 。

如果有新内容,你可以向用户显示一个按钮来执行更新,或者自动执行更新。你需要确保释放陈旧的资源。

如果列表为null,则以下脚本会更新所有需要更新的 catalogs:

public static AsyncOperationHandle<List<IResourceLocator>> UpdateCatalogs(IEnumerable<string> catalogs = null, bool autoReleaseHandle = true)

返回值是更新的定位器列表。

你可能还想删除由于更新 catalogs 而不再引用的任何 bundle 缓存条目。如果是这样,请使用以下版本的 UpdateCatalogs API,其中可以启用额外参数autoCleanBundleCache以移除任何不需要的缓存数据:

public static AsyncOperationHandle<List<IResourceLocator>> UpdateCatalogs(bool autoCleanBundleCache, IEnumerable<string> catalogs = null, bool autoReleaseHandle = true)

有关 bundle 缓存的更多信息,请参阅 AssetBundle caching 。

有关运行时更新内容的更多信息,请参阅 Unique Bundle IDs setting 。

内容更新示例

以下讨论通过一个假设的例子说明了 Addressable 内容在内容更新期间如何处理。在此示例中,考虑一个已发布的应用程序,它包含以下 Addressables Group :

Local_StaticRemote_StaticRemote_NonStatic
AssetAAssetLAssetX
AssetBAssetMAssetY
AssetCAssetNAssetZ

Local_StaticRemote_Static属于“不能在发布后更改” Group 。

由于这个版本是实时的,现有玩家在他们的设备上拥有Local_Static,并且可能缓存了远程包。

如果修改每组中的一个资源(AssetAAssetLAssetX),然后运行“检查内容更新限制”,本地 Addressable 设置中的结果如下:

Local_StaticRemote_StaticRemote_NonStaticcontent_update_group(non-static)
AssetXAssetA
AssetBAssetMAssetYAssetL
AssetCAssetNAssetZ

准备操作编辑了“不能在发布后更改”组,这可能看起来不合常理。然而,系统会构建上述布局,但会丢弃任何此类组的构建结果。因此,从玩家的角度来看,你最终会得到以下内容:

Local_Static
AssetA
AssetB
AssetC

Local_Static包已经存在于玩家设备上,你无法更改。此旧版本的 AssetA 不再被引用。相反,它作为无用数据停留在玩家设备上。

Remote_Static
AssetL
AssetM
AssetN

Remote_Static包未更改。如果它尚未缓存到玩家设备上,它将在请求 AssetM 或 AssetN 时下载。像 AssetA 一样,旧版本的 AssetL 不再被引用。

Remote_NonStatic (old)
AssetX
AssetY
AssetZ

Remote_NonStatic 包现在是旧的。你可以从服务器删除它或留在那里;无论哪种方式,它都不会被下载。如果已缓存,它将无限期地保留在玩家设备上,除非你删除它。请参阅 AssetBundle caching 以获取更多信息。像 AssetA 和 AssetL 一样,旧版本的 AssetX 不再被引用。

Remote_NonStatic (new)
AssetX
AssetY
AssetZ

旧的 Remote_NonStatic 包已被一个新版本替换,通过其哈希文件区分。修改后的 AssetX 版本已使用此新包更新。

content_update_group
AssetA
AssetL
content_update_group 包含将来会被引用的已修改资源。

上述示例有以下含义:

  1. 任何更改的本地资源将永远保留在用户设备上但不会被使用。
  2. 如果用户已经缓存了一个非静态包,他们将需要重新下载该包,包括未更改的资源(例如 AssetY 和 AssetZ)。理想情况下,用户没有缓存该包,这样他们只需要下载新的 Remote_NonStatic 包。
  3. 如果用户已经缓存了Static_Remote包,他们只需下载更新的资源(例如,通过content_update_groupAssetL)。这在这种情况下是理想的。如果用户没有缓存该包,他们必须通过 content_update_group 下载新的 AssetL 和通过未更改的Remote_Static包下载现在无效的 AssetL。无论初始缓存状态如何,用户设备上总会有无效的 AssetL,被无限期缓存尽管从未被访问。

你的远程内容的最佳设置将取决于你的具体使用案例。

内容更新依赖关系(Content update dependencies)

直接更改资源并不是标记需要作为内容更新的一部分重建的唯一方式。更改资源的依赖关系是一个不太明显的因素,在构建更新时会被考虑。

例如,考虑上例中的 Local_Static 组:

Local_Static
AssetA
AssetB
AssetC

假设此组中的资源具有如下的依赖链:AssetA 依赖于 Dependency1,Dependency1 依赖于 Dependency2,AssetB 依赖于 Dependency2,AssetC 依赖于 Dependency3,所有三个依赖项都是 Addressable 和非 Addressable 资源的混合。

如果只更改 Dependency1 并运行“检查内容更新限制”,结果项目结构如下:

Local_Staticcontent_update_group
AssetA
AssetB
AssetC

如果只更改 Dependency2:

Local_Staticcontent_update_group
AssetA
AssetB
AssetC

最后,如果只更改 Dependency3:

Local_Staticcontent_update_group
AssetA
AssetB
AssetC

这是因为当依赖项更改时,整个依赖树需要重建。

以下示例具有此依赖树。AssetA 依赖于 AssetB,AssetB 依赖于 Dependency2 ,AssetC 依赖于 Dependency3 。现在,如果 Dependency2 更改,项目结构如下:

Local_Staticcontent_update_group
AssetA
AssetB
AssetC

这是因为 AssetA 依赖于 AssetB,而 AssetB 依赖于 Dependency2。由于整个链需要重建,AssetA 和 AssetB 都会被放入 content_update_group

Content Update Builds Settings

要发布内容更新,你的应用程序必须已经使用 Remote Catalog 并将其远程内容托管在可访问的服务器上。请参阅 Enabling remote distribution 以获取有关设置内容托管和分发的信息。

你还应该考虑如何设置每个组的Update Restriction设置。这些设置决定了Check for Content Update Restriction工具如何处理你组中的更改内容。选择适当的设置有助于尽量减少内容更新的下载大小。

请参阅 Group Update Restriction settings 设置以获取更多信息。

更新先前的构建设置(Update a Previous Build setting)

Addressable Asset Settings 还包含一个用于更新先前构建的部分:
在这里插入图片描述

设置描述
Check For Update Issues告知系统是否应自动运行Check For Content Update Restrictions检查,以及如何处理检测到的问题。
Content State Build Path此位置有两个目的:* 指示新内容构建将先前的状态文件(previous state file)放在哪里。
  • 这是“更新先前的构建”尝试自动从中提取先前状态文件的位置。
    |
    如果你希望在服务器上有一个共享的先前状态文件,内容状态构建路径(Content State Build Path)可以是远程位置。系统以不同方式处理先前状态文件的远程位置:

  • 新内容构建将 previous state file 放置在 ContentUpdateScript.PreviousContentStateFileCachePath,默认路径为 Library/com.unity.addressables/AddressablesBinFileDownload/

  • 更新先前的构建(Update a Previous Build)下载 remote previous state file 到 ContentUpdateScript.PreviousContentStateFileCachePath,然后像正常一样读取文件。如果远程位置不存在文件,但已放置在缓存路径中,系统会加载本地文件。

组更新限制设置(Group Update Restriction settings)

对于项目中的每个组,Update Restriction模式决定了在内容更新中如何处理组及其资源:

  • 防止更新(Prevent Updates):启用时,系统将组中的资源视为 Static Content ,你预计这些内容很少甚至不会更新。所有 local content 都应该使用此设置

根据组中的内容类型以及你预计在应用程序的完整构建之间更新内容的频率来选择设置。

你可以更改 Group 中的内容,无论你选择哪种设置。区别在于 [Check for Content Update Restrictions](https://questerai.feishu.cn/wiki/X1RPwif66iJ9p8k5IpxcXRr9nPe#part-OiGpdxlIWor4hLxWYttcaAYCnlf)[Update a Previous Build](https://questerai.feishu.cn/wiki/X1RPwif66iJ9p8k5IpxcXRr9nPe#part-CJKAdoHpqo7DAExqi1Rc6qZFn6e) 工具如何处理组中的资源以及已安装的应用程序如何访问更新的内容。

除非你在执行完整构建,否则不要更改组的Update Restriction设置。如果你在内容更新之前更改 Group 的该设置,Addressables 无法生成更新构建所需的正确更改。

Prevent Updates Enabled (Static Content)

启用Prevent Updates时,Check for Content Update Restrictions工具会将任何更改的资源移动到新 Group ,该 Group 设置为从你的远程路径构建和加载。这是与**Update a Previous Build**自动集成的相同检查。无论你是手动运行工具,还是让Update a Previous Build自动处理检查,内容更新都会设置 Remote Catalog ,以便从新包访问更改的资源,但仍从原始包访问未更改的资源。

将你预计不会经常更新的内容组织到设置为“防止更新”的组中。你可以安全地设置这些组以生成更少、更大的包,因为你的用户通常只需要下载这些包一次。

将任何你打算从本地加载路径加载的组设置为“防止更新”。同样,将任何生成大远程包的组设置为Prevent Updates,这样即使你最终更改了这些组中的资源,你的用户也只需要下载更改的资源

Prevent Updates Disabled (dynamic content)

当组没有启用Prevent Updates时,如果组内任何资源发生更改,内容更新会重建整个包。Update a Previous Build脚本会设置目录,以便已安装的应用程序从新包中加载组中的所有资源。

将你预计经常更改的内容组织到未启用防止更新的组中。因为当任何单个资源更改时,所有这些组中的资源都会重新发布,你应该设置这些组以生成包含较少资源的较小包

唯一包 ID 设置(Unique Bundle IDs setting)

如果你想在应用程序启动时而不是在启动后更新内容,请使用唯一包 ID 设置。启用此选项可以更轻松地在应用程序会话中间加载更新的 AssetBundles,但通常会使构建速度变慢并增加更新的大小。

启用唯一包 ID 选项允许你在原始包仍在内存中时加载更改版本的 AssetBundle。使用唯一内部 ID 构建 AssetBundles 使你可以在运行时更轻松地更新内容,而不会遇到 AssetBundle ID 冲突

但是,启用后,包含引用更改资源的资源的任何 AssetBundles 也必须重新构建。更多包必须更新以进行内容更新,所有构建速度都变慢。

当你在 Addressable 系统初始化后更新内容目录并开始加载资源时,通常只需要使用唯一包 ID。

你可以使用以下方法之一避免 AssetBundle 加载冲突和启用唯一 ID 的需要:

  • 将内容目录更新作为 Addressables 初始化的一部分。默认情况下,只要你没有在 Addressable 资源设置中启用“仅手动更新目录”选项,Addressables 会在初始化时检查新目录。选择此方法会排除在会话中间更新应用程序内容。
  • 在更新内容目录之前卸载所有远程 AssetBundles。卸载所有远程包和资源也可以避免包名冲突,但可能会中断用户的会话,因为他们需要等待新内容加载。

使用持续集成(Continuous Integration)构建 Addressables

你可以使用持续集成(CI)系统执行 Addressables 内容构建和应用程序 Player 构建。本页面提供了使用 CI 系统构建 Addressables 的一般指南。

选择内容构建器(Content Builder)

构建 Addressables 内容时,主要选择之一是选择内容构建器。默认情况下,如果你调用 AddressableAssetSettings.BuildPlayerContent,它将使用 BuildScriptPackedMode 脚本作为 IDataBuilder 实例。BuildPlayerContent 方法会检查 ActivePlayerDataBuilder 设置,并调用该脚本的 BuildDataImplementation

如果你已经实现了自己的自定义 IDataBuilder 并希望在 CI 构建中使用它,请设置 AddressableAssetSettingsActivePlayerDataBuilderIndex 属性。默认情况下,你可以通过 AddressableAssetSettingsDefaultObject.Settings 访问正确的设置实例。此索引指的是 AddressableAssetSettings.DataBuilders 列表中 IDataBuilder 的位置。以下代码示例演示了如何设置自定义 IDataBuilder

#if UNITY_EDITOR
    using UnityEditor.AddressableAssets;
    using UnityEditor.AddressableAssets.Build;
    using UnityEditor.AddressableAssets.Settings;
    using UnityEngine;

    internal class CustomDataBuilder
    {
        public static void SetCustomDataBuilder(IDataBuilder builder)
        {
            AddressableAssetSettings settings
                = AddressableAssetSettingsDefaultObject.Settings;

            int index = settings.DataBuilders.IndexOf((ScriptableObject)builder);
            if (index > 0)
                settings.ActivePlayerDataBuilderIndex = index;
            else if (AddressableAssetSettingsDefaultObject.Settings.AddDataBuilder(builder))
                settings.ActivePlayerDataBuilderIndex
                    = AddressableAssetSettingsDefaultObject.Settings.DataBuilders.Count - 1;
            else
                Debug.LogWarning($"{builder} could not be found " +
                                 $"or added to the list of DataBuilders");
        }
    }
#endif

Clean Addressables Content Builder Cache

IDataBuilder 实现定义了一个 [ClearCachedData](https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEditor.AddressableAssets.Build.IDataBuilder.ClearCachedData.html#UnityEditor_AddressableAssets_Build_IDataBuilder_ClearCachedData) 方法,用于清理该数据构建器创建的任何文件。例如,默认的 BuildScriptPackedMode 脚本会删除以下内容:

  • 内容目录(content catalog)
  • 序列化设置文件(serialized settings file)
  • 构建的 AssetBundles
  • 任何创建的 link.xml 文件

你可以在 CI 过程中调用 IDataBuilder.ClearCachedData 以确保构建不使用先前构建生成的文件。

Clean Scriptable Build Pipeline Cache

清理脚本化构建管道(SBP)缓存会清理 Library 目录中的 BuildCache 文件夹以及构建生成的所有哈希映射和类型数据库。Library/BuildCache 文件夹包含 SBP 在构建过程中创建的 .info 文件,通过读取这些 .info 文件的数据而不是重新生成未更改的数据,加快了后续构建的速度。

要在脚本中清除 SBP 缓存而不打开提示对话框,请调用 BuildCache.PurgeCache(false)

当使用命令行参数或通过持续集成构建 Addressables 内容或 Player 构建时,你应为每个目标平台重新启动 Unity 编辑器。这确保 Unity 在脚本编译完成后才能调用 -executeMethod。有关使用命令行参数的更多信息,请参阅 Unity 用户手册文档中的命令行参数。


http://www.kler.cn/news/294937.html

相关文章:

  • 85、 探针
  • Java基础 1. Java开发环境搭建
  • 数据处理与数据填充在Pandas中的应用
  • 基于 RocketMQ 的云原生 MQTT 消息引擎设计
  • 智能体叙事实验:MixlabNodes新增Her页面
  • Android --- observer和observerForever的区别
  • Ansible自动化运维入门:从基础到实践的全面指南
  • 福建聚鼎科技:开一家装饰画店铺需要投资多少钱
  • Java|Java 中 JSONPath 的使用
  • history增加时间显示
  • PostgreSQL的repmgr工具介绍
  • centos基本命令
  • 店匠科技携手Stripe共谋电商支付新篇章
  • 漫谈设计模式 [14]:迭代器模式
  • 人工智能与机器学习原理精解【16】
  • Mac M1 安装Hadoop教程(安装包安装)
  • keepalived和lvs高可用集群
  • 一次关于生产环境服务无故宕机的排查过程
  • 顶刊精析|METI:整合细胞形态与空间转录组学的肿瘤微环境分析框架·24-09-06
  • MySQL面试题大全和详解,含SQL例子
  • EF框架删除数据
  • Hive是什么?
  • 模型压缩之剪枝
  • Openharmony 图片自适应全屏显示
  • C++系统教程002-数据类型(01)
  • pytorch torch.gather函数介绍
  • 运维工程师面试题--Linux加分项
  • Mysql(一) - 数据库操作, 表操作, CRUD
  • CMU 10423 Generative AI:lec3(阅读材料:GPT1论文解读)
  • 申万宏源证券完善金融服务最后一公里闭环,让金融服务“零距离、全天候”