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

App Thinning

Apple Store および iOS は、ユーザーのデバイスの性能に応じて配信するアプリを切り替えることによって iOS、tvOS、watchOS アプリケーションのインストールを最適化しています。この最適化は App Thinning と呼ばれます。App Thinning によって、デバイスの機能の大部分を利用しつつ使用するディスク領域を最小限に留め、Apple の更新にも対応するアプリケーションの作成が可能となります。この最適化のプロセスに関する詳細は、 Apple Developer Library の App Thinning の項 をご覧ください。

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

オンデマンドリソース

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

アセットバンドルはモデルやマテリアル、テクスチャー、シーンなどのアセットファイルを含む場合がありますが、スクリプトを含むことはできません。したがって、スクリプトのロジックは全てメインのアプリケーション内にある必要があります。 Apple は、 (特に 3G での)読み込み時間を短縮してデバイス上で使用されるストレージ領域を最小限に抑えるために、アセットバンドルのサイズを 64MB 以下にすることを推奨しています。

ODR のセットアップ

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

まだ iOS が選択されていない場合は、 iOS をクリックし、 Switch Platform ボタンをクリックしてください。次に Player Settings… ボタンをクリックし、インスペクターウィンドウで Other Settings を開いて、 Configuration のセクションで Use on demand resources のチェックボックスをオンにしてください。

アセットバンドルの作成

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

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

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

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

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

この新しいアセットバンドルファイルを生成するには、上記で作成した新しいレベルがエディタースクリプトで参照される必要があります。エディタースクリプトを作成するには、プロジェクトウィンドウ内に “Editor” という新しいフォルダーを作成します。この “Editor” フォルダー上で右クリックし、 Create > C# Script の順に選択します。新しいスクリプトに BuildiOSAssetBundles.cs と名付けてください。

この例では、 “Textures” フォルダーに “textures” というラベルが割り当てられています。 BuildiOSAssetBundles.cs という新しいスクリプトが “Editor” フォルダー内に作成されています。

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 フォルダー内にアセットバンドルが生成されます。

以下のスクリプトは ODR アセットバンドル “textures” をダウンロードし、それをパブリックメンバー 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 スライシング のプロセスもオンデマンドリソース(ODR)のプロセスと似ており、アプリケーションの実行されているデバイスの仕様に応じてアセットを動的にダウンロードできるようにするものです。(例えば、 Retina iPad 用に高解像度アセットをダウンロードし、 iPhone や iPad Mini 等の小さいデバイス用には低解像度アセットをダウンロードするなど。)これは、アセットバンドルに付加的な バリアント(variant) を設定することによって行います。こうすることで、どのバリアントを使用するかを起動時に決定し、ダウンロード時に自動でそれをアセットファイル名に付加することができるようになります。

バリアント(Variant)を作成するには、新しいフォルダーをクリックし、インスペクターウィンドウ下部の Asset Labels のセクションで、右側のドロップダウンメニューをクリックして New… を選択し、新しいバリアントの名前を入力してください。([注]アセットバンドルのバリアントは小文字である必要があります。)

この新しいバリアントはエディタースクリプトで参照される必要があります。エディタースクリプトを作成するには、プロジェクトウィンドウ内で “Editor” という新しいフォルダーを作成します。この “Editor” フォルダーを右クリックし、 Create > C# Script の順に選択します。新しいスクリプトに BuildiOSAssetBundles.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( );
    }
}

アセットバンドルとオンデマンドリソース(ODR)についての詳細は、Bitbucket 上で提供している Unity の アセットバンドルマネージャー デモプロジェクト をご覧ください。ランディングページに、デモの使用および調整方法の包括的な説明が掲載されています。

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