面白い方へ転がっていくブログ

TouchDesignerとかUnityとか色々使って面白そうな方向へ転がっていきます。

HoloLensでAssetBundleDemoを動かす

皆さんお馴染みのAssetBundle。
AssetBundleはちゃんと使おうと思ったら色々と闇が深そうですが、とりあえずUnity Technologiesさんが公開しているAssetBundleDemoをHoloLensで動かしてみたいと思います。

なおAssetBundleDemoは以下にあります。

bitbucket.org

AssetBundleDemoの中でも、今回は「AssetBundleLoader」シーンを動かしてみたいと思います。AssetBundleLoaderシーンを動かすための手順は大きく以下の3つです。

  1. AssetBundleをビルドする
  2. AssetBundleServerを起動する
  3. デプロイ & 実行する

この手順自体はHoloLensであっても、他のプラットフォームであってもそう変わらないと思います。 ですが、UWP/HoloLens向けに多少ソースコードを修正しないと実行できないので、手順を追いながら適宜HoloLensで実行できるように変更していきたいと思います。

手順1:AssetBundleをビルドする

まずはAssetBundleとしてサーバーへ配置するファイル群を生成します。

ビルドターゲットをUWPへ変更する

f:id:gesyutapo:20171120170234p:plain
BuildSettingsのPlatformをUWPへ変更

Unityのメニューバーから[File]-[Build Settings]を選択すると、上記のダイアログが表示されます。 この画像の通り、Platformから「Windows Store」を選択し、「Switch Platform」ボタンを押下します。 UnityのロゴマークWindows Storeのところに表示されたら、変更完了です。

AssetBundleビルドする

Unityのメニューバーから[Assets]-[AssetBundles]-[Build AssetBundles]を選択します。

f:id:gesyutapo:20171220185756p:plain

これでUnityのプロジェクトディレクトリに「AssetBundles」ディレクトリが生成され、その中にアセットバンドルファイルが生成されて・・・いません

どうやらビルドに失敗して、UnityのConsoleに以下のエラーが表示されてしまうようです。

ArgumentNullException: Argument cannot be null.
Parameter name: path2
System.IO.Path.Combine (System.String path1, System.String path2) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.IO/Path.cs:115)
AssetBundles.BuildScript.CreateAssetBundleDirectory () (at Assets/AssetBundleManager/Editor/BuildScript.cs:19)

【修正1】UWP向けのプラットフォーム名を返すようにする

エラーメッセージを見ると、「Assets/AssetBundleManager/Editor/BuildScript.cs」の19行目、System.IO.Path.Combine()を呼び出しているところの引数path2がnullのようです。 ここをソースコードで確認すると以下のような感じになっています。

static public string CreateAssetBundleDirectory()
{
    // Choose the output path according to the build target.
    string outputPath = Path.Combine(Utility.AssetBundlesOutputPath, Utility.GetPlatformName());
    if (!Directory.Exists(outputPath))
        Directory.CreateDirectory(outputPath);

    return outputPath;
}

Path.Combine()の引数path2の部分は恐らく Utility.GetPlatformName() の部分だと思われます。 このUtilityクラスは「Assets/AssetBundleManager/Utility.cs」にあるらしいので、そちらを見てみます。

public static string GetPlatformName()
{
#if UNITY_EDITOR
    return GetPlatformForAssetBundles(EditorUserBuildSettings.activeBuildTarget);
#else
    return GetPlatformForAssetBundles(Application.platform);
#endif
}

このGetPlatformForAssetBundlesがnullになっているために、エラーが発生していそうです。 このメソッドはビルドターゲットやアプリケーションのプラットフォームに応じて、そのプラットフォームに対応する文字列を返してくれるっぽいです。

つまり先に答えを言ってしまうと、このメソッドの中では各プラットフォーム別にswitchで対応文字列を返しているのですが、UWP向けの条件がないためにnullが返ってきています。 と、いうワケで以下のコード中のコメントで // Add と書いている部分を4行ほど追加してUWPへ対応させてみましょう。

private static string GetPlatformForAssetBundles(BuildTarget target)
{
    switch (target)
    {
        case BuildTarget.Android:
            return "Android";
#if UNITY_TVOS
        case BuildTarget.tvOS:
            return "tvOS";
#endif
        case BuildTarget.iOS:
            return "iOS";
        case BuildTarget.WebGL:
            return "WebGL";
        case BuildTarget.WebPlayer:
            return "WebPlayer";
        case BuildTarget.StandaloneWindows:
        case BuildTarget.StandaloneWindows64:
        case BuildTarget.WSAPlayer:                  // Add
            return "Windows";
        case BuildTarget.StandaloneOSXIntel:
        case BuildTarget.StandaloneOSXIntel64:
        case BuildTarget.StandaloneOSXUniversal:
            return "OSX";
        // Add more build targets for your own.
        // If you add more targets, don't forget to add the same platforms to GetPlatformForAssetBundles(RuntimePlatform) function.
        default:
            return null;
    }
}
#endif

private static string GetPlatformForAssetBundles(RuntimePlatform platform)
{
    switch (platform)
    {
        case RuntimePlatform.Android:
            return "Android";
        case RuntimePlatform.IPhonePlayer:
            return "iOS";
#if UNITY_TVOS
        case RuntimePlatform.tvOS:
            return "tvOS";
#endif
        case RuntimePlatform.WebGLPlayer:
            return "WebGL";
        case RuntimePlatform.OSXWebPlayer:
        case RuntimePlatform.WindowsWebPlayer:
            return "WebPlayer";
        case RuntimePlatform.WindowsPlayer:
        case RuntimePlatform.WSAPlayerARM:         // Add
        case RuntimePlatform.WSAPlayerX64:         // Add
        case RuntimePlatform.WSAPlayerX86:         // Add
            return "Windows";
        case RuntimePlatform.OSXPlayer:
            return "OSX";
        // Add more build targets for your own.
        // If you add more targets, don't forget to add the same platforms to GetPlatformForAssetBundles(RuntimePlatform) function.
        default:
            return null;
    }
}

【修正2】GetStreamingAssetsPath()で適当なパスを返すようにする

上記を修正しても、今度は以下のようなエラーが表示されてAssetBundleのビルドに失敗します。

Assets\AssetBundleManager\AssetBundleManager.cs(162,56): error CS0117: 'Environment' does not contain a definition for 'CurrentDirectory'

問題となるコードはAssets/AssetBundleManager/AssetBundleManager.csのようです。

private static string GetStreamingAssetsPath()
{
    if (Application.isEditor)
        return "file://" +  System.Environment.CurrentDirectory.Replace("\\", "/"); // Use the build output folder directly.
    else if (Application.isWebPlayer)
        return System.IO.Path.GetDirectoryName(Application.absoluteURL).Replace("\\", "/") + "/StreamingAssets";
    else if (Application.isMobilePlatform || Application.isConsolePlatform)
        return Application.streamingAssetsPath;
    else // For standalone player.
        return "file://" + Application.streamingAssetsPath;
}

上記コードの中の

return "file://" +  System.Environment.CurrentDirectory.Replace("\\", "/"); // Use the build output folder directly.

のところで、System.Environment.CurrentDirectoryが存在しない定義だと怒られているようです。

これはUnityがHoloLens用にビルドする際の.NetCore1.1ではSystem.Environment.CurrentDirectoryプロパティが存在しないためにエラーになっているようです。 .NetCoreのドキュメントを見ると、確かに.NetCore2.0からSystem.Environment.CurrentDirectoryプロパティが追加されたようなので、.NetCore1.1では存在しないようです。

しかしここのコード、実は何にしても影響ないので、先ほどのコードを適当なものへ変更します。

private static string GetStreamingAssetsPath()
{
    if (Application.isEditor)
        return string.Empty;      // ← 適当に変更
    else if (Application.isWebPlayer)
        return System.IO.Path.GetDirectoryName(Application.absoluteURL).Replace("\\", "/") + "/StreamingAssets";
    else if (Application.isMobilePlatform || Application.isConsolePlatform)
        return Application.streamingAssetsPath;
    else // For standalone player.
        return "file://" + Application.streamingAssetsPath;
}

はい、適当にstring.Emptyにしちゃいました。 真面目にやるならSystem.Environment.CurrentDirectoryに代わる、.NetCore1.1で使用可能なカレントディレクトリを取得する方法を用いて設定してあげましょう。

改めてAssetBundleビルドを行う

上記修正が終わったら、改めてAssetBundleのビルドを行います。 再びUnityのメニューバーから[Assets]-[AssetBundles]-[Build AssetBundles]を選択します。

これでUnityのプロジェクトディレクトリに「AssetBundles」ディレクトリが生成され、その中にアセットバンドルファイルが生成されていると思います。

f:id:gesyutapo:20171220190958p:plain

手順2:AssetBundleServerを起動する

Unityのメニューバーの[Assets]-[AssetBundles]-[Local AssetBundle Server]を選択します。

f:id:gesyutapo:20171220191428p:plain

これで先ほどの「AssetBundles」ディレクトリの中身が公開されるようになります。

ServerとなるPCのIPアドレスを指定する

上記のLocal AssetBundle Serverを動かしているPCのIPアドレスを設定して、HoloLensからアクセス出来るようにします。

Assets/AssetBundleSample/Scripts/LoadAssets.csに指定します。

// Initialize the downloading URL.
// eg. Development server / iOS ODR / web URL
void InitializeSourceURL()
{
    // If ODR is available and enabled, then use it and let Xcode handle download requests.
    #if ENABLE_IOS_ON_DEMAND_RESOURCES
    if (UnityEngine.iOS.OnDemandResources.enabled)
    {
        AssetBundleManager.SetSourceAssetBundleURL("odr://");
        return;
    }
    #endif
    #if DEVELOPMENT_BUILD || UNITY_EDITOR
    // With this code, when in-editor or using a development builds: Always use the AssetBundle Server
    // (This is very dependent on the production workflow of the project.
    //      Another approach would be to make this configurable in the standalone player.)
    AssetBundleManager.SetDevelopmentAssetBundleServer();
    return;
    #else
    // Use the following code if AssetBundles are embedded in the project for example via StreamingAssets folder etc:
    AssetBundleManager.SetSourceAssetBundleURL(Application.dataPath + "/");
    // Or customize the URL based on your deployment or configuration
    //AssetBundleManager.SetSourceAssetBundleURL("http://www.MyWebsite/MyAssetBundles");
    return;
    #endif
}

上記コードの19行目あたりから、以下のように変更します(抜粋)。

#else
// Use the following code if AssetBundles are embedded in the project for example via StreamingAssets folder etc:
//AssetBundleManager.SetSourceAssetBundleURL(Application.dataPath + "/");      // ←コメントアウト
// Or customize the URL based on your deployment or configuration
AssetBundleManager.SetSourceAssetBundleURL("http://(ServerPCのIPアドレス):7888/");   // ←コメントアウトを外してIPアドレスを指定
return;
#endif

接続先は

http://(ServerPCのIPアドレス):7888/

となります。

UWPのインターネット受信許可を与える

UWPでは、セキュリティの関係でそのままではインターネットアクセスなどが行えません。これはAndroidiOSなんかと一緒で、必要な機能の許可設定をしてあげる必要があります。 今回はAssetBundleServerとなるPCへの接続を行うため、「InternetClient」の許可を行います。

まずはUnityのメニュバーで[Edit]-[Project Settings]-[Player]を選択します。

f:id:gesyutapo:20171220201600p:plain

選択するとInspectorパネルにPlayerSettingsが表示されるので、「Publishing Settings」を選択して「InternetClient」を選択してチェックを入れます。

f:id:gesyutapo:20171220202057p:plain

手順3:デプロイ&実行する

これで一通りの準備が整いました。 HoloLensへデプロイして、実行してみましょう。

f:id:gesyutapo:20171220200737j:plain

これでひとまずHoloLensでAssetBundleManagerを利用したAssetBundle機能を使うことが出来ました。


参考URL

mtaulty.com