【Mac】UnityからGoogleDrive内のファイルをインポートする方法を超丁寧に解説する

GoogleDrive内に置いてあるマスターデータJsonファイルをUnityにインポートしたい。

手動でGoogleDriveからダウンロードしてUnityエディタに入れても良いんだけど、更新するたびに手動でやるのは面倒なので、できれば自動化したい。

ということで、やってみました。

元々の意図はJsonファイルのインポートですが、この記事ではJsonファイルに限りません。

タイトル通り、MacでUnityからGoogleDrive内のファイルをインポートする方法を、超丁寧に解説します。

GoogleApisをダウンロードする

まずはGoogleApisのDLLファイルをダウンロードします。

Windowsなら簡単にできそうなのですが、MacでGoogleApisをダウンロードする場合は一手間必要です。

(ここの上手い方法が分からなかったので、もしもっと簡単な方法があったら教えて欲しいです)

まず、Visual Studio for Macが入っていない場合は、こちらからインストールしてください。

Visual Studio Code(VSCode)は別物なので注意。

GoogleApisはVisualStudioを通してダウンロードします。

VisualStudioをインストールできたら、起動して「ファイル」 > 「新しいソリューション」から新規プロジェクトを作成します。

「Web and Console」の「アプリ」を選択し、「コンソール アプリケーション」を選択した上で次へ。

対象のフレームワークはデフォルトのままで次へ。

プロジェクト名と保存先を適当に入力。プロジェクト名と同じ文字列が自動でソリューション名にも入力されます。

その他はデフォルトのままにして、作成を選択。

エディタが開かれるので、左のソリューションパネルの中にある「依存関係」を右クリック。

その中にある「NuGetパッケージの管理…」を選択。

「NuGetパッケージの管理 – InstallGoogleApi」というウィンドウが開かれるので、左上のプルダウンが「nuget.org」になっていることを確認した上で、以下のパッケージを追加します。

  • Google.Apis
  • Google.Apis.Auth
  • Google.Apis.Core
  • Google.Apis.Drive.v3
  • Google.Apis.Script.v1
  • Google.Apis.Sheets.v4
  • Newtonsoft.json

右上に検索窓があるので、検索して見つけてください。

上記リストの項目全てにチェックを入れ、「パッケージの追加」で追加できます。

パッケージの追加には数秒かかりますが、ダウンロードが終わるとソリューションパネルの「依存関係」の子要素に先ほどのパッケージが入っているのが分かります。

対象パッケージのダウンロードが終わったら、左上にある実行ボタン(再生ボタン)を押して、一度実行します。

デフォルトで記述されている「Hello, World!」をコンソールに表示するだけのプログラムが実行されますが、これによってフォルダ内にDLLファイルが生成されます。

生成されたものをFinderで確認しましょう。

「依存関係」の親要素を右クリックし、「Finderで表示」をクリック。

すると、こんな感じで bin/Debug/netcoreapp3.1 内にいくつかファイルが生成されているのが分かります。

最後に、生成されたファイルのうち先ほどのリストにあるDLLファイルをUnityエディタにドラッグ&ドロップします。

今回はPlugins/GoogleApisというフォルダを作ってその中に入れました。

場所は好きなところで大丈夫です。

Google Cloud Platformを設定する

次に、Google Cloud Platformの設定をします。

Google Cloud Platformのページに移動し、GoogleDriveにアクセスした時と同じGoogleアカウントでログインします。

「Google Cloud Platform」の右にある「プロジェクトの選択」をクリック。
(一度も利用したことがない方はもしかしたら文言が違うかも?)

「プロジェクトの選択」モーダルが開かれるので、右上の「新しいプロジェクト」をクリック。

プロジェクト名を適当に入力します。

場所は「組織なし」でOK。

「作成」をクリックします。

規約の同意が求められる場合は問題なければ同意し、左のメニューから「APIとサービス」を選択。

「OAuth同意画面」をクリックし、「外部」を選んで「作成」。

アプリケーション名を適当に入力し、「保存」をクリック。

ちなみにアプリケーション名に「Google」という文字列が入っているとエラーになるため、「Google」などの文字列を含まないアプリケーション名にする必要があるようです。

次に、「認証情報」から「認証情報を作成」をクリックし、「OAuthクライアントID」をクリック。

アプリケーションの種類は「デスクトップアプリ」を選択し、適当にアプリケーション名を入力したら「作成」。

「OAuth クライアントを作成しました」というモーダルが表示されたら閉じます。

「OAuth 2.0 クライアントID」の右端にあるダウンロードアイコンをクリックし、ダウンロードされた設定jsonファイルをUnityエディタ内に入れておきます。

再びGoogle Cloud Platformに戻り「ダッシュボード」を選択、そして上部にある「APIを有効化」をクリック。

APIライブラリのページに行くので、Google Drive APIを選択し、「有効にする」をクリック。

これでGoogle Cloud Platformでの設定は終わりました。

Unityでの実装

長い道のりでしたが、ついにUnityでの実装に入ります。

まず、以下の3ファイルをUnityエディタ内のEditorフォルダ直下に作成します。

FileImporter.cs

using System.Threading;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;

/// <summary>
/// GoogleDriveからファイルをインポート
/// </summary>
public static class FileImporter
{
    /// <summary>
    /// インポート先
    /// </summary>
    private static string DESTINATION_PATH = "./Assets/destination/path.json";

    /// <summary>
    /// GoogleDriveにある対象のファイルID
    /// </summary>
    private static string DRIVE_FILE_ID = "YOUR_DRIVE_FILE_ID";

    /// <summary>
    /// インポートしたい時に呼び出す
    /// </summary>
    [MenuItem("Tools/GoogleDriveからインポート")]
    private static async void Import()
    {
        ImportProgress.Init();
        var context = SynchronizationContext.Current;
        await Task.Run(() =>
        {
            ImportProgress.UpdateStatus(ImportProgress.ProgressStatus.Downloading);
            context.Post(_ =>
                {
                    GoogleDriveImporter.Import(DRIVE_FILE_ID, DESTINATION_PATH,
                        () => ImportProgress.UpdateStatus(ImportProgress.ProgressStatus.Finished));
                }, null);
        });
    }
}

GoogleDriveImporter.cs

using System;
using UnityEditor;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using Google.Apis.Services;
using Google.Apis.Download;
using Google.Apis.Util.Store;
using System.IO;
using System.Threading;

/// <summary>
/// GoogleDriveからファイルをインポート
/// </summary>
public static class GoogleDriveImporter
{
    /// <summary>
    /// GoogleDriveAPIの設定ファイル
    /// </summary>
    private static string CLIENT_SECRET_FILE = "./Assets/client/secret/path/client_secret.json";

    /// <summary>
    /// GoogleDriveAPIの出力ファイル
    /// </summary>
    private static string CREDENTIAL_FILE = "./Assets/credential/path/credential.json";

    /// <summary>
    /// GoogleDriveからインポート
    /// </summary>
    public static void Import(string fileID, string destPath, Action onComplete)
    {
        // スコープを設定
        string[] scopes = { DriveService.Scope.DriveReadonly };
        
        // 認証
        UserCredential credential;
        using (var stream = new FileStream(CLIENT_SECRET_FILE, FileMode.Open, FileAccess.Read))
        {
            credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                GoogleClientSecrets.Load(stream).Secrets,
                scopes,
                "user",
                CancellationToken.None,
                new FileDataStore(CREDENTIAL_FILE, true)).Result;
        }

        // GoogleDriveAPIに必要
        var service = new DriveService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = "default name",
        });

        // ファイル取得
        var request = service.Files.Get(fileID);
        var output = new FileStream(destPath, FileMode.Create, FileAccess.Write);
        request.MediaDownloader.ProgressChanged += (IDownloadProgress progress) =>
        {
            switch (progress.Status)
            {
                case DownloadStatus.Completed:
                {
                    // 成功
                    ImportProgress.UpdateStatus(ImportProgress.ProgressStatus.Converting);
                    ImportProgress.Post(() =>
                    {
                        AssetDatabase.ImportAsset(destPath);
                        AssetDatabase.SaveAssets();
                        AssetDatabase.Refresh();
                        output.Close();
                        onComplete?.Invoke();
                    });
                    break;
                }
                case DownloadStatus.Failed:
                {
                    // 失敗
                    UnityEngine.Debug.LogError("インポートに失敗しました: " + progress.Exception);
                    ImportProgress.UpdateStatus(ImportProgress.ProgressStatus.Error);
                    break;
                }
            }
        };
        request.Download(output);
    }
}

ImportProgress.cs

using System.Threading;
using UnityEditor;
using UnityEngine.Events;

/// <summary>
/// インポートの進度を表示
/// </summary>
public static class ImportProgress
{
    /// <summary>
    /// 進度
    /// </summary>
    public enum ProgressStatus
    {
        Preparing,
        Downloading,
        Converting,
        Finished,
        Error,
    }

    /// <summary>
    /// SynchronizationContext
    /// </summary>
    private static SynchronizationContext context;

    /// <summary>
    /// 初期化
    /// </summary>
    public static void Init()
    {
        context = SynchronizationContext.Current;
        UpdateStatus(ProgressStatus.Preparing);
    }

    public static void Post(UnityAction action)
    {
        context.Post(_ => action?.Invoke(), null);
    }

    /// <summary>
    /// 進度を更新
    /// </summary>
    public static void UpdateStatus(ProgressStatus status)
    {
        context.Post(_ =>
        {
            switch (status)
            {
                case ProgressStatus.Preparing:
                    EditorUtility.DisplayProgressBar("インポート中", "ファイル作成中...", 0.1f);
                    break;
                case ProgressStatus.Downloading:
                    EditorUtility.DisplayProgressBar("インポート中", "ファイルダウンロード中...", 0.4f);
                    break;
                case ProgressStatus.Converting:
                    EditorUtility.DisplayProgressBar("インポート中", "ファイル展開中...", 0.8f);
                    break;
                case ProgressStatus.Finished:
                case ProgressStatus.Error:
                    EditorUtility.ClearProgressBar();
                    break;
            }
        }, null);
    }
}

FileImporter.csのDESTINATION_PATHには、インポート先のフォルダパスを指定してください。

同じくFileImporter.csのDRIVE_FILE_IDには、インポートするGoogleDrive内のファイルIDを指定します。

ファイルIDはGoogleDriveの対象ファイルを右クリックして「共有」を選択し、
コピーしたリンク「https://drive.google.com/file/d/XXXXXXXXXXXXX/view?usp=sharing」の「XXXXXXXXXXXXX」部分です。

GoogleDriveImporter.csのCLIENT_SECRET_FILEには、先ほどダウンロードした設定jsonファイルのパスを指定します。

そのままだとファイル名が長いので、client_secret.jsonという名前にリネームしました。

同じくGoogleDriveImporter.csのCREDENTIAL_FILEはGoogleDriveAPIの処理をした時に出力される(特に気にしなくて良い)ファイルのパスです。

パスはどこでも良いので、CLIENT_SECRET_FILEと同じフォルダとかにしておきましょう。

エディタでインポートを実行する

ようやくインポート処理を実行します。

Unityエディタのメニューバーから Tools > GoogleDriveからインポート を選択します。

プログレスバーが表示された直後、初回実行時はブラウザが開いて認証を求められます。

先ほどから使っているGoogleアカウントでログインし、いろいろ許可していきます。

上の画像のように警告が出た場合は、左下の「詳細」をクリックして詳細を開き、「FileImporter(安全ではないページ)に移動」をクリック。

もとのページに戻るので、出てきたダイアログで「許可」を選択。

Received verification code. You may now close this window.

というメッセージが表示されたら認証は完了なので、タブを閉じてUnityエディタに戻ります。

すると、指定したパスにダウンロードされたファイルが出力されているはずです。

コメント