The Apple store and iOS optimize the installation of iOS, tvOS, and watchOS apps by tailoring app delivery to the capabilities of the user’s particular device. This optimization is called app thinning. App thinning lets you create apps that use the most device features, occupy minimum disk space, and accommodate future updates that can be applied by Apple. Learn more about this optimization process on Apple’s Developer Library page on App Thinning.
本章介绍可以在 Unity 中实现的应用程序精简的两个主要组件:
按需加载资源 (ODR) 是适用于 iOS 和 tvOS 平台(从 iOS 和 tvOS 9.0 版开始)的功能。此功能允许将核心资源(应用程序启动时需要的资源)与可选资源(或者出现在游戏后期关卡中的资源)分开,从而减小应用程序的大小。这些附加资源称为 AssetBundles。它们可用于所有 Unity 构建目标,但必须采取额外步骤才能通过 App Store 托管它们。
AssetBundles can contain Asset files such as models, Materials, Textures and Scenes, but they cannot include scripts. This means that 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 3G) and minimize the amount of storage space used on the device.
要对项目进行 ODR 设置,请首先确认构建类型设置为 iOS。在菜单栏中,选择 File > Build Settings。
If it isn’t selected already, click on iOS, then click the Switch Platform button. Next, click the Player Settings… button. In the Inspector window, open Other Settings, navigate to the Configuration section, and check the Use on demand resources checkbox.
首先,创建一个新文件夹以放置需要添加到 AssetBundle 的资源。要执行此操作,请在 Project 窗口内右键单击,然后选择 Create > Folder__(或单击 Project 窗口左上角的 Create__ > __Folder__)。
选择要添加到 AssetBundle 的资源文件,然后将它们拖放到新文件夹中。
创建 AssetBundle 时,需要为其分配一个标签,该标签将在请求下载 AssetBundle 时用作标识符。最好创建一个与捆绑包的文件名匹配的标签;这样可以确保标签的唯一性,并便于以后使用时进行识别。
To create or assign a tag, select your new folder and navigate to the Asset Labels section at the bottom of the Inspector window. Click the left-hand drop-down menu, select New…, and enter the name of your new label. Note that AssetBundle labels must be lower-case.
In this example, a folder called “Textures” is being given a new label
To generate the new AssetBundle file, the new label must be referenced 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 been given the label “textures”. The new script BuildiOSAssetBundles.cs has been created in the “Editor” folder.
Open BuildiOSAssetBundles.cs and copy in the code below. In this example the label “textures” has been used; change this throughout with the name of your label (in lower-case text):
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 );
}
}
The significant line in the above code sample is the following, which takes the files tagged with the “textures” label and creates an AssetBundle file called “textures” within the Assets/ODR folder:
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 has already been built - for example, from another project or a third-party vendor:
new UnityEditor.iOS.Resource( "bundle", "Assets/Bundles/bundle.unity3d" ).AddOnDemandResourceTags( "bundle" )
The whole code sample creates a new menu in the Unity Editor menu bar. Go to Bundle > Build iOS AssetBundle. This generates the AssetBundles in the ODR folder.
The following script downloads the “textures” ODR Asset Bundle, 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 中的构建版本即可查看相关的更多信息:
App slicing follows a similar process to on-demand resourcing, allowing you to dynamically download Assets according the specification of the device the app is running on - for example, to download high-resolution Assets for retina iPads, and low-resolution Assets for smaller devices like iPhones and the iPad Mini. This is achieved by defining AssetBundles, with the added provision of variants. This way, you can decide at startup which variant to use, and automatically append this to the Asset file name upon download.
To create a Variant, click on your new folder and navigate to the Asset Labels section at the bottom of the Inspector window. Click on the right-hand drop-down menu, select New…, and enter the name of your new variant. Note that AssetBundle variants must be lower-case.
The new variant must be referenced 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 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.hd", "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( );
}
}
要了解有关 AssetBundles 和按需加载资源的更多信息,请参阅 Bitbucket 上托管的 Unity AssetBundle Manager 演示项目。登陆页面提供了有关如何使用和调整演示项目的全面说明。