Version: 2022.2
Build for iOS
在 iOS 设备上进行故障排除

应用程序精简

Apple 商店和 iOS 可根据用户具体设备的性能来定制 iOS、tvOS 和 watchOS 应用程序的分发,从而优化这些应用程序的安装。此优化称为应用程序精简。借助应用程序精简,可以使创建的应用程序利用最多的设备功能、占用最少的磁盘空间并适应 Apple 未来可能应用的更新。请参阅 Apple 关于应用程序精简的开发者库页面,了解有关此优化过程的更多信息。

Unity lets you implement the following two major components for app thinning:

按需加载资源

On-demand resources (ODR) is a feature available for the iOS and tvOS platforms, from version 9.0 of iOS and tvOS onwards. It allows you to reduce the size of your application by separating the core Assets (those that are needed from application startup) from Assets which may be optional, or which appear in later levels of your game. These additional Assets are called AssetBundles that are available across all Unity build targets, but you must take additional steps to enable them for hosting from the App Store.

AssetBundles can contain Asset files such as models, Materials, Textures, and Scenes, but they can’t include scripts. Therefore, all your scripting logic must reside in the main application. Apple recommends that an AssetBundle be no larger than 64MB in size, to reduce loading time (particularly over-the-air) and minimize the amount of storage space used on the device.

设置 ODR

要对项目进行 ODR 设置,请首先确认构建类型设置为 iOS。在菜单栏中,选择 File > Build Settings

如果尚未选中相关选项,请单击 iOS__,然后单击 Switch Platform__ 按钮。接下来,单击 Player Settings 按钮。在 Inspector 窗口中,打开 Other Settings__,导航到 Configuration__ 部分,然后启用 Use on demand resources 复选框。

创建 AssetBundle

首先,创建一个新文件夹以放置需要添加到 AssetBundle 的资源。要执行此操作,请在 Project 窗口内右键单击,然后选择 Create > Folder__(或单击 Project 窗口左上角的 Create__ > __Folder__)。

选择要添加到 AssetBundle 的资源文件,然后将它们拖放到新文件夹中。

创建 AssetBundle 时,需要为其分配一个标签,该标签将在请求下载 AssetBundle 时用作标识符。最好创建一个与捆绑包的文件名匹配的标签;这样可以确保标签的唯一性,并便于以后使用时进行识别。

要创建或分配标签,请选择新文件夹,然后导航到 Inspector 窗口底部的 Asset Labels 部分。单击左侧下拉菜单,选择 __New__,然后输入新标签的名称。请注意,AssetBundle 标签必须为小写字母。

In this example, a folder called Textures is being given a new label.

To generate the new AssetBundle file, you must references the new label in an Editor script. To create an Editor script, create a new folder inside the Project window called Editor. Right-click on the Editor folder and select Create > C# Script. Name the new script BuildiOSAssetBundles.cs.

In this example, the Textures folder has the label textures and the new script BuildiOSAssetBundles.cs is created in the Editor folder.

Open BuildiOSAssetBundles.cs and copy in the code below. In this example, the label textures is used; change this throughout with the name of your label (in lower-case text).

Note that this example uses uncompressed bundles; however, this isn’t a requirement for app thinning.

using UnityEngine;
using UnityEditor;


public class BuildiOSAssetBundles : MonoBehaviour
{
    [InitializeOnLoadMethod]
    static void SetupResourcesBuild( )
    {
        UnityEditor.iOS.BuildPipeline.collectResources += CollectResources;
    }

    static UnityEditor.iOS.Resource[] CollectResources( )
    {
        return new UnityEditor.iOS.Resource[]
        {
            new UnityEditor.iOS.Resource( "textures", "Assets/ODR/textures" ).AddOnDemandResourceTags( "textures" ),
            new UnityEditor.iOS.Resource( "bundle", "Assets/Bundles/bundle.unity3d" ).AddOnDemandResourceTags( "bundle" ),
        };
    }

    [MenuItem( "Bundle/Build iOS AssetBundle" )]
    static void BuildAssetBundles( )
    {
        var options = BuildAssetBundleOptions.None;

        bool shouldCheckODR = EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS;

# if UNITY_TVOS
            shouldCheckODR |= EditorUserBuildSettings.activeBuildTarget == BuildTarget.tvOS;
# endif

        if( shouldCheckODR )
        {
# if ENABLE_IOS_ON_DEMAND_RESOURCES
            if( PlayerSettings.iOS.useOnDemandResources )
                options |= BuildAssetBundleOptions.UncompressedAssetBundle;
# endif

# if ENABLE_IOS_APP_SLICING
            options |= BuildAssetBundleOptions.UncompressedAssetBundle;
# endif
        }

        BuildPipeline.BuildAssetBundles( "Assets/ODR", options, EditorUserBuildSettings.activeBuildTarget );
    }

}

上面代码示例中的重要代码行如下,其中使用带有 textures 标签的文件,并在 Assets/ODR 文件夹中创建名为 textures 的 AssetBundle 文件:

new UnityEditor.iOS.Resource( "textures", "Assets/ODR/textures" ).AddOnDemandResourceTags( "textures" )

For demonstration purposes, the above code sample also includes the following line, which adds an AssetBundle called bundle that’s already built - for example, from another project or a third-party vendor:

new UnityEditor.iOS.Resource( "bundle", "Assets/Bundles/bundle.unity3d" ).AddOnDemandResourceTags( "bundle" )

整个代码示例将在 Unity Editor 菜单栏中创建一个新菜单。选择 Bundle > Build iOS AssetBundle。此时将在 ODR 文件夹中生成 AssetBundles。

The following script downloads the textures ODR AssetBundle, assigning it to the public member TextureBundle. Place this somewhere in your project.

using UnityEngine;
using UnityEngine.iOS;
using System;
using System.Collections;

public class LoadBundle : MonoBehaviour
{
    public AssetBundle     TextureBundle;


    void Start( )
    {
        StartCoroutine( LoadAsset( "textures", "textures" ) );
    }

    public IEnumerator LoadAsset( string resourceName, string odrTag )
    {
        // 创建请求
        OnDemandResourcesRequest request = OnDemandResources.PreloadAsync( new string[] { odrTag } );

        // 等待请求完成
        yield return request;

        // 检查是否有错误
        if( request.error != null )
            throw new Exception( "ODR request failed: " + request.error );

        TextureBundle = AssetBundle.LoadFromFile( "res://" + resourceName );

        request.Dispose( );
    }
}

下一步是生成 Xcode 项目,构建 .IPA,并将其上传到 iTunes Connect 的 TestFlight。在 TestFlight 处理过程中,嵌入式 ODR AssetBundles 将从应用程序中删除并托管在 Apple 的服务器上,可供下载。

在 Xcode 中构建 .IPA 之前,请检查 XCode 的 Build Settings,确保 Assets 部分中的 Embed Asset packs In Product Bundle 设置为 No__,而 Enable On Demand Resources__ 设置为 Yes

iTunes Connect 处理完应用程序的上传后,单击 TestFlight Builds 中的构建版本即可查看相关的更多信息:

应用程序切片

应用程序切片遵循与按需加载资源类似的流程,允许根据运行应用程序的设备的规格动态下载资源(例如,下载用于 Retina iPad 的高分辨率资源,以及用于 iPhone 和 iPad Mini 等小型设备的低分辨率资源)。这是通过定义 AssetBundles 以及添加__变体__配置来实现的。这样可以在启动时决定使用哪种变体,并在下载时自动将其附加到资源文件名。

要创建变体,请单击新文件夹,然后导航到 Inspector 窗口底部的 Asset Labels 部分。单击右侧下拉菜单,选择 __New__,然后输入新变体的名称。请注意,AssetBundle 变体必须为小写字母。

必须在 Editor 脚本中引用新变体。要创建 Editor 脚本,请在 Project 窗口中创建一个名为 Editor 的新文件夹。右键单击 Editor 文件夹,然后选择 Create > C# Script。将新脚本命名为 BuildiOSAppSlices.cs

复制并粘贴下面的代码,将示例标签(“textures”)和变体(“hd”和“sd”)替换为您自己的标签和变体。在此代码示例中,引用了多个文件夹:一个包含 HD 纹理,一个包含 SD 纹理。它们分别被赋予了变体“hd”和“sd”。

using UnityEngine;
using UnityEditor;


public class BuildiOSAppSlices : MonoBehaviour
{
    [InitializeOnLoadMethod]
    static void SetupResourcesBuild( )
    {
        UnityEditor.iOS.BuildPipeline.collectResources += CollectResources;
    }

    static UnityEditor.iOS.Resource[] CollectResources( )
    {
        return new UnityEditor.iOS.Resource[]
        {
            new UnityEditor.iOS.Resource("textures").BindVariant( "Assets/ODR/textures.hd", "hd" )
                                     .BindVariant( "Assets/ODR/textures.sd", "sd" )
                     .AddOnDemandResourceTags( "textures" ),
    };
    }

    [MenuItem( "Bundle/Build iOS App Slices" )]
    static void BuildAssetBundles( )
    {
        var options = BuildAssetBundleOptions.None;

        bool shouldCheckODR = EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS;

# if UNITY_TVOS
            shouldCheckODR |= EditorUserBuildSettings.activeBuildTarget == BuildTarget.tvOS;
# endif

        if( shouldCheckODR )
        {
# if ENABLE_IOS_ON_DEMAND_RESOURCES
            if( PlayerSettings.iOS.useOnDemandResources )
                options |= BuildAssetBundleOptions.UncompressedAssetBundle;
# endif

# if ENABLE_IOS_APP_SLICING
            options |= BuildAssetBundleOptions.UncompressedAssetBundle;
# endif
        }

        BuildPipeline.BuildAssetBundles( "Assets/ODR", options, EditorUserBuildSettings.activeBuildTarget );
    }

}

这段代码将在 Unity Editor 菜单栏中创建一个名为 Bundle 的新菜单。单击此菜单并选择列表中唯一的菜单项 Build iOS App Slices。此时将在 ODR 文件夹中生成 AssetBundles。

然后,要加载资源,请将此类放在项目中的某个位置,并传入要加载的变体的名称:

using UnityEngine;
using UnityEngine.iOS;
using System;
using System.Collections;

public class LoadBundle : MonoBehaviour
{
    public AssetBundle     TextureBundle;


    void Start( )
    {
        StartCoroutine( LoadAsset( "textures", "textures" ) );
    }

    public IEnumerator LoadAsset( string resourceName, string odrTag )
    {
        // 创建请求
        OnDemandResourcesRequest request = OnDemandResources.PreloadAsync( new string[] { odrTag } );

        // 等待请求完成
        yield return request;

        // 检查是否有错误
        if( request.error != null )
            throw new Exception( "ODR request failed: " + request.error );

        TextureBundle = AssetBundle.LoadFromFile( "res://" + resourceName );

        request.Dispose( );
    }
}

You can now see and modify variants in Player Settings > Other Settings > Configuration in a Variant map for app slicing drop-down menu. The Variant map for app slicing menu is visible only if Use on demand resources is enabled in Player Settings.

Additional resources:

Build for iOS
在 iOS 设备上进行故障排除