1
1

【Unity】StreamingAssetsに配置したファイルをAndroidで実行時に読み込む

Last updated at Posted at 2024-08-10

はじめに

想定読者様はこちらです。

  • StreamingAssets(ストリーミングアセッツ)に配置したファイルをAndroidビルド時に使用したいお方
  • 具体的にどのような仕組みでAndroid実行時に読み込んでいるのか知りたいお方

調べてみたところ方法は書いてあっても順序を追って仕組みを説明してくださる方は見かけなかったので確認の意味合いも込めて仕組みについても言及させていただきます。

折りたたみ
折りたたみになっている個所は自身の解釈を織り交ぜて解説をしております。
間違い等のご指摘は大変に歓迎しておりますのでお時間のある方はぜひお願いいたします。

実装

解説するに際してこのようなものを作成してみました。

StreamingAssetsPathFormatter.cs
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using Cysharp.Threading.Tasks;

/// <summary>
/// 要求に適したStreamingAssetsパスを返却します
/// </summary>
public static class StreamingAssetsPathConfigurator
{
    // キャッシュしたアンドロイドパスを保存
    private static List<string> _androidCachePath = new();

    /// <summary>
    /// StreamingAssetsのファイルにアクセスするためのファイルパスを返却します
    /// </summary>
    /// <param name="relativePath">指定ファイルのStreamingAssetsからの相対パス</param>
    /// <returns></returns>
    public static async UniTask<string> GetStreamingAssetsPathAsync(string relativePath)
    {
        // Android実行時jarファイルのURIを取得
        string resultPath = @$"{Application.streamingAssetsPath}/{relativePath}";

#if UNITY_ANDROID
        // デバイス上のパブリックディレクトリを取得する
        string persistentDataPath = @$"{Application.persistentDataPath}/{relativePath}";

        // キャッシュしてあるパスなら早期リターン
        if (_androidCachePath.Contains(persistentDataPath))
        {
            return persistentDataPath;
        }

        var request = UnityWebRequest.Get(resultPath);
        
        // UniTaskの機能で非同期に読み込む
        await request.SendWebRequest();

        // 読み込んだデータをパブリックディレクトリへ移す
        File.WriteAllBytes(persistentDataPath, request.downloadHandler.data);

        // 結果のパスをパブリックディレクトリのパスに書き換え
        resultPath = persistentDataPath;

        // 一度取得したファイルへのパスはキャッシュする
        _androidCachePath.Add(resultPath);
#endif

        return resultPath;
    }
}

上記のプログラムを一言で説明するとGetStreamingAssetPathAsyncを使用するユーザーが求めていると予想されるパスを返却するものです。

解説

順に解説させていただきます。

1. GetStreamingAssetsPathAsyncの引数について

まずrelativePathについてですがこれはStreamingAssetsから読み込みたいファイルへの相対パスを入力します。

例えば、下記のようなディレクトリ構成であった場合relativePathAudios/Click.wavとなります。

StreamingAssets/
 └Audios/
   └Click.wav

2. Application.streamingAssetsPathの仕様

次に返却するためのパスを@$"{Application.streamingAssetsPath}/{relativePath}"で取得します。

ここでStreamingAssetsの仕様を知る必要が発生します。

その仕様とはAndroidビルド時にApplication.streamingAssetsPathはあらかじめStreamingAssetsに配置したアセットを、jarファイルとして圧縮したデータが配置されているURIを返却するということです。

URIとは

URIURL(Web上における住所のようなもの)とURN(識別するための名前)が組み合わさったものです。
URLがパス、URNがファイル名だといえばわかりやすいでしょうか。
一般的なWebページにアクセスするためのURLURNを含んでいるので、実はURLではなくURIなのです。

jarファイルとは

jarファイルとはjavaでよく使用されているものです。
zip形式とほぼ同じ圧縮方式であるjar形式で圧縮されています。
zipファイルとの違いは解凍しなくても使用することができる点などがあげられます。

仕様を確認したところで解説を再開します。

3. #if UNITY_ANDROIDについて

#if UNITY_ANDROIDというプリプロセッサディレクティブを使用することで条件付きコンパイルを行い、Androidビルド時に特別な処理を行っています。

プリプロセッサディレクティブと条件付きコンパイル

プリプロセッサディレクティブを使用することでコンパイルされる情報を特定の条件に応じて変更することができます。
これを条件付きコンパイルと呼び、無用なコンパイルを避けることができます。

ところで#if UNITY_ANDROID && !UNITY_EDITORを使わないことに違和感を持たれた方も少なくないのではないでしょうか。

ここにつきましては私が以下のように考えているからです。

  • リクエストを行わずパフォーマンスを向上させたとしてもAndroidで稼働させるときには意味がないこと
  • 及び、実機より性能が良いと考えられる開発側で処理が重いと感じる場合は処理を見直す必要があると考えられること
    (見直しの必要を発見することも遅れると考えられます)
  • また、記述ミスなどでエラーが発生している場合もエラーの発見が遅れること

無用なファイルの生成、および、リクエストを行うことにつながることも事実ですのでご自由に書き換えていただければと考えております。

4. Application.persistentDataPathの仕様

続いてstring persistentDataPath = @$"{Application.persistentDataPath}/{relativePath}";について解説させていただきます。

こちらはコメントに書いてある通りデバイス上のパブリックディレクトリの位置を取得しています。
セーブデータなどを保存するときはここに保存することも多いようです。

5. UnityWebRequestを利用したjarファイルの読み込み

続いて……

var request = UnityWebRequest.Get(resultPath);
await request.SendWebRequest();

こちらですが@$"{Application.streamingAssetsPath}/{relativePath}で取得したURIを使用してjarファイルを取得しています。

UniTaskawaitを使用して処理を待機していますが、IEnumeratorを使ったコルーチンであればyield returnを使用して待機することも可能です。

6. パブリックディレクトリへのjarファイルのデータ書き込み

そしてFile.WriteAllBytes(persistentDataPath, request.downloadHandler.data);を行うことで、byte[]型でjarファイルの内容を取得し、デバイス上のパブリックディレクトリに書き込んでいます。

7. 疑似的なStreamingAssetsPathの返却

続いてresultPath = persistentDataPath;を行うことで疑似的にStreamingAssetsのファイルパスを取得したように見せかけています。

8. パスのキャッシュ

そして_androidCachePath.Add(resultPath);で一度jarファイルを取得しパブリックディレクトリに書き込んだことのあるファイルパスはキャッシュされ……

if (_androidCachePath.Contains(persistentDataPath))
{
    return persistentDataPath;
}

を行うことでキャッシュ済みのパスか確認を行い、初回のみリクエストを行うことで不要なリクエストを避けています。

最後に

AndroidでStreamingAssetsを使用する際に困った方は少なくないと思います。

この記事が皆様のお役に立てれば幸いです。

今年(2024年)の夏は大変に暑くなっております。
皆様ご自愛ください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1