iOS の WWW リクエストのカスタマイズ
iOS のマネージスタックトレース

App Thinning

Apple App Store と iOSは、ユーザーの特定のデバイスの機能に合わせてアプリを配信することによって iOS、tvOS、watchOS アプリケーションのインストールを最適化します。この最適化は “App thinning” と呼ばれます。App thinning を使用すると、ほとんどのデバイス機能を使用し、使用するディスク容量が最小で、Apple が行う将来の更新に対応できるアプリケーションを作成できます。この最適化プロセスの詳細については、App Thinning に関する Apple Developer Library ページ を参照してください。

ここでは、Unity に利用できる App thinning の 2 つの主要コンポーネントについて説明します。

オンデマンドリソース

オンデマンドリソース (ODR) は、iOS と tvOS のバージョン 9.0 以降で利用可能な iOS と tvOS プラットフォーム用の機能です。ODR を使用すると、主要なアセット (アプリケーションの起動時から必要) を必須ではないアセットや、後のレベルで使用されるアセットと分けることによって、アプリケーションのサイズを削減することができます。これらの必須ではないアセットは アセットバンドル と呼ばれます。アセットバンドルは Unity の全てのビルドターゲットで利用できますが、App Store 経由でホスティング可能にするためには追加の手順が必要です。

アセットバンドルには、モデル、マテリアル、テクスチャ、シーンなどのアセットファイルを含めますが、スクリプトを含めることはできません。つまり、すべてのスクリプトロジックは主要なアプリケーションに存在する必要があります。Apple では、アセットバンドルのサイズを 64MB 以下にすることを推奨しています。これにより、読み込み時間を削減し (特に無線で)、デバイスで使用するストレージ容量を最小限に抑えることができます。

ODR の設定

プロジェクトを ODR 用に設定するには、まず、ビルドタイプが iOS に設定されていることを確認してください。メニューバーから File > Build Settings の順に選択します。

まだ選択されていない場合は、iOS をクリックし、Switch Platform ボタンをクリックします。次に、Player Settings ボタンをクリックします。 Inspector ウィンドウで Other Settings を開き、Configuration セクションに移動し、Use on demand resources チェックボックスにチェックを入れます。

アセットバンドルの作成

まず、AssetBundle (アセットバンドル) に入れたいアセットをまとめるための新しいフォルダーを 1 つ作成してください。フォルダーを作成するには、Project ウィンドウ内で右クリックし、 Create > Folder (または、Project ウィンドウ左上で Create > Folder) の順に選択してください。

アセットバンドルに追加したいアセットファイルを選択し、新しく作成したフォルダーにそれらをドラッグアンドドロップします。

アセットバンドル作成の際は、それにタグを 1 つ割り当てる必要があります。このタグは、そのアセットバンドルのダウンロードをリクエストする際の識別子として使用されます。バンドルのファイル名と一致するラベルを作成することをお勧めします。そうすれば確実に他と重複しないラベルとなり、後に使用する際に特定し易くなります。

タグを作成したり割り当てるには、新しいフォルダーを選択し、 Inspector ウィンドウの下部にある Asset Labels セクションに移動します。左のドロップダウンメニューをクリックし、New を選択し、新しいラベルの名前を入力します。アセットバンドルのラベルは小文字にする必要があります。

この例では、Textures という名前のフォルダーに新しいラベルが与えられています。

新しいアセットバンドルファイルを生成するには、エディタースクリプトで新しいラベルを参照する必要があります。エディタースクリプトを作成するには、Project ウィンドウの Editor 内に新しいフォルダーを作成します。 Editor フォルダーを右クリックし、Create > C# Script の順に選択します。新しいスクリプトの名前を BuildiOSAssetBundles.cs にします。

この例では、Textures フォルダーに textures というラベルが付けられています。新しいスクリプト BuildiOSAssetBundles.csEditor フォルダーに作成されました。

BuildiOSAssetBundles.cs を開き、以下のコードをコピーします。この例では、textures というラベルが使用されています。これをラベルの名前 (小文字のテキスト) に変更します。

この例では非圧縮バンドルを使っています。ただし、これは 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 というアセットバンドルファイルを作成します。

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

デモの目的で、上のコードサンプルには、以下の行も含まれています。この行は、すでにビルドされている (例えば、別のプロジェクトやサードパーティベンダーから取得した) bundle というアセットバンドルを加えます。

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

コードサンプル全体が、Unity エディターのメニューバーに新しいメニューを作成します。 Bundle > Build iOS AssetBundle の順に移動します。これにより、ODR フォルダーにアセットバンドルが生成されます。

以下のスクリプトは textures ODR アセットバンドルをダウンロードし、パブリックメンバーの 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 プロジェクトを 1 つ生成し、.IPA を 1 つビルドしてそれを iTunes Connect の TestFlight にアップロードする方法です。TestFlight の処理手続の一環として、埋め込まれた ODR アセットバンドルがアプリケーションから削除されて Apple のサーバー上にホスティングされ、ダウンロードが可能な状態になります。

Xcode で .IPA をビルドする前に、まず XCode の Build Settings (ビルド設定) の Assets のセクションで Embed Asset packs In Product BundleNo に設定されていることと、Enable On Demand ResourcesYes に設定されていることを確認してください。

iTunes Connect によるアプリケーションのアップロード処理が完了したら、 TestFlight の Builds のセクションで該当のバージョンのビルドをクリックして詳細情報を確認します。

App Slicing

App slicing はオンデマンドリソースに似た処理を行い、アプリケーションが実行されているデバイスの仕様に基づいて動的にアセットをダウンロードすることができます (例えば、Retina iPad では、高解像度アセットをダウンロードし、iPhone や iPad Mini などの小さい解像度のデバイスでは、低解像度アセットをダウンロードするなど)。これは、アセットバンドルを定義し、追加の variants を定義することで実現されます。こうすることで、起動時にどのバリアントを使用するかを決めることができ、ダウンロード時に自動的にアセットファイル名に加えることができます。

バリアントを作成するには、作成した新しいフォルダーをクリックし、Inspector ウィンドウ下部の Asset Labels セクションに移動します。右のドロップダウンメニューをクリックし、New を選択し、新しいバリアントの名前を入力します。アセットバンドルのバリアント名は小文字にする必要があります。

新しいバリアントはエディタースクリプトで参照される必要があります。エディタースクリプトを作成するには、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 エディターのメニューバーに Bundle というメニュー項目が新しく作成されます。これをクリックし、リストにある唯一のアイテム Build iOS App Slices を選択してください。これにより ODR フォルダー内にアセットバンドルが生成されます。

その後、このクラスをプロジェクトのどこかに配置し、読み込みたいバリアントの名前を渡すと、アセットが読み込まれます。

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( );
    }
}

Player Settings > Other Settings > ConfigurationVariant map for app slicing でバリアントを表示、変更することができます (プレイヤー設定で Use on demand resources が有効になっている場合にのみメニューが表示されます) 。

アセットバンドルとオンデマンドリソースについての詳細は、Bitbucket の Unity の AssetBundleDemo プロジェクト を参照してください。ページに、デモの使用および調整方法の包括的な説明が掲載されています。


iOS の WWW リクエストのカスタマイズ
iOS のマネージスタックトレース