Apple 商店和 iOS 可根据用户具体设备的性能来定制 iOS、tvOS 和 watchOS 应用程序的分发,从而优化这些应用程序的安装。此优化称为应用程序精简。借助应用程序精简,可以使创建的应用程序利用最多的设备功能、占用最少的磁盘空间并适应 Apple 未来可能应用的更新。请参阅 Apple 关于应用程序精简的开发者库页面,了解有关此优化过程的更多信息。
本章介绍可以在 Unity 中实现的应用程序精简的两个主要组件:
按需加载资源 (ODR) 是适用于 iOS 和 tvOS 平台(从 iOS 和 tvOS 9.0 版开始)的功能。此功能允许将核心资源(应用程序启动时需要的资源)与可选资源(或者出现在游戏后期关卡中的资源)分开,从而减小应用程序的大小。这些附加资源称为 AssetBundles。它们可用于所有 Unity 构建目标,但必须采取额外步骤才能通过 App Store 托管它们。
AssetBundles 可以包含资源文件,例如模型、材质、纹理和场景,但不能包含脚本。这意味着所有脚本逻辑都必须位于主应用程序中。为减少加载时间(特别是在无线条件下)并最大限度减少设备上使用的存储空间,Apple 建议 AssetBundle 的大小不要超过 64MB。
要对项目进行 ODR 设置,请首先确认构建类型设置为 iOS。在菜单栏中,选择 File > Build Settings。
如果尚未选中相关选项,请单击 iOS__,然后单击 Switch Platform__ 按钮。接下来,单击 Player Settings 按钮。在 Inspector 窗口中,打开 Other Settings__,导航到 Configuration__ 部分,然后启用 Use on demand resources 复选框。
首先,创建一个新文件夹以放置需要添加到 AssetBundle 的资源。要执行此操作,请在 Project 窗口内右键单击,然后选择 Create > Folder__(或单击 Project 窗口左上角的 Create__ > __Folder__)。
选择要添加到 AssetBundle 的资源文件,然后将它们拖放到新文件夹中。
创建 AssetBundle 时,需要为其分配一个标签,该标签将在请求下载 AssetBundle 时用作标识符。最好创建一个与捆绑包的文件名匹配的标签;这样可以确保标签的唯一性,并便于以后使用时进行识别。
要创建或分配标签,请选择新文件夹,然后导航到 Inspector 窗口底部的 Asset Labels 部分。单击左侧下拉菜单,选择 __New__,然后输入新标签的名称。请注意,AssetBundle 标签必须为小写字母。
在此示例中,一个名为 Textures 的文件夹被赋予了新标签
要生成新的 AssetBundle 文件,必须在 Editor 脚本中引用新标签。要创建 Editor 脚本,请在 Project 窗口中创建一个名为 Editor 的新文件夹。右键单击 Editor 文件夹,然后选择 Create > C# Script。将新脚本命名为 BuildiOSAssetBundles.cs。
在此示例中,为 Textures 文件夹赋予了标签 textures。在 Editor 文件夹中创建了新脚本 BuildiOSAssetBundles.cs。
打开 BuildiOSAssetBundles.cs 并将以下代码复制到其中。此示例中使用了标签 _textures_;请用所需的标签名称(小写文本)通改此名称。
请注意,此示例使用未压缩的捆绑包;但这并非应用程序精简的要求。
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" )
出于演示目的,上面的代码示例还包括以下代码行,用于添加一个已经构建的名为 bundle 的 AssetBundle(例如,来自其他项目或第三方供应商):
new UnityEditor.iOS.Resource( "bundle", "Assets/Bundles/bundle.unity3d" ).AddOnDemandResourceTags( "bundle" )
整个代码示例将在 Unity Editor 菜单栏中创建一个新菜单。选择 Bundle > Build iOS AssetBundle。此时将在 ODR 文件夹中生成 AssetBundles。
以下脚本下载 textures ODR Asset Bundle,并将其分配给公共成员 TextureBundle。将此包放在项目的某个位置。
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( );
}
}
现在可以在 Player Settings > Other Settings > Configuration 中的 Variant map for app slicing 下拉菜单中查看和修改变体(仅当在 Player Settings 中启用了 Use on demand resources 时,此菜单才可见)。
要了解有关 AssetBundles 和按需加载资源的更多信息,请参阅 Bitbucket 上托管的 Unity AssetBundle Manager 演示项目。登陆页面提供了有关如何使用和调整演示项目的全面说明。
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.