UnityでGoogle SpreadsheetへアクセスしたいVSCode民が色々頑張って成功した話

最近Unityで少しずつ手探りでゲームを作ってみています。ほんとーに少しずつです。

その過程で、シナリオデータを作って読み込みたい時期になってきました。

ひとまず、UnityからGoogle Spreadsheet上のデータを読み込めるようにしようということで、色々と模索して実装できたよという話をしたいと思います。

実装方法ですが、GoogleAPI Client Libraryを公開してくれているため、コード自体はかなり簡単に書けます。ただし、このClient LibraryはNuGetパッケージとして公開されていることから、Unityで使えるようにするためにひと苦労せねばなりませんでした。

特に私は、NuGetパッケージを今まで活用したことがなく、しかもVIsual StudioではなくVisual Studio Code(VSCode)で開発していたため、NuGetパッケージのインストール方法がよくわからず、その点でも混乱ポイントがありました。

というわけで、ちょぴっと頑張ったのでその軌跡をブログに残しておきたくなったワケです。

前提

以下の作業はすでに終わっているものとして話を進めます。

  • GCP上でサービスアカウントを作成しており、Sheet APIは有効化済み、Credentialsは手元に用意してある
  • データを読み取りたいスプシにてサービスアカウントへ権限を付与済み

また、Unityは2022.3.4f1を使用しています。

MicrosoftがUnity向けの手順ページを公開していた

NuGetでインストールしたいパッケージのページを見てみると、インストール用のコマンドが記載されています。例えばGoogle.Apis.Sheets.v4のページではこんな感じ

Visual Studio経由でのインストールだとPackage Manager Consoleを使ってコマンド一発でインストールできるみたいですが、こちらはVSCodeの民です。また、.NET CLIでのインストールも試みましたが、エラー続きで萎えたので撤退。UnityでしかC#使ったことないからそういうのわかんないんだわ。

どうしたものかと調べてみたところ、Microsoft公式がこんなページを用意してました。

learn.microsoft.com

お、それっぽいそれっぽい。ということで、こちらのページを参考にしつつ進めていきます。

Unityで.NET 4.xを有効にする

まずはUnityのPlayer Settingsにて.NET 4.xを有効にせよとのことで、Edit > Project Settings > Player > Other Settings を見てみます。公式の言うところによると以下のようにApi compatibility Levelで.NET 4.xを選択せよとのことでしたが、

実際に自分の環境で見てみると、.NET 4.xが選択肢にありません。

どうやら、既に.NET 4.xにApi compatibility Levelが切り替えられた状態だとこのような表記になるみたいです。特に自分では設定してないので、最近のUnityではデフォルトで.NET 4.xが設定されてるみたいですね。その場合、.NET Frameworkを選択することが.NET 4.xに該当するようなので、.NET Frameworkを選択します。

選択後は設定反映を確実にするためUnityを再起動します。

NuGetパッケージのdllを入手する

次に、NuGetパッケージのdllを入手します。

該当のNuGetパッケージのページにて"Download package"をクリックすると .nupkg形式のファイルがダウンロードされます。

このファイルの拡張子を.zipに書き換えて解凍せよとのこと。解凍して出てきたフォルダ内のlib/net45配下にほしいdllファイルがありました。こいつをUnityに持っていきます。

.nupkgってそんな風にして開けるんですね。これは自分では思いつかなかっただろうなあ。

今回のスプシ読み取り用途で必要だったパッケージは以下です。

※ 後ほど知ったのですが、VSCodeNuGet Package ManagerのExtentionがあるみたいです。これを使えばよかったのでは、、、

Unityにdllファイルを配置する

Assets/Plugins配下に入手したdllファイルたちをブチ込みます。

読み込み処理終了後、特にエラーが出てないことを確認しつつ、Unityを再起動。

すると、Client LibraryへのAutocompleteが発動するようになってました!やったね!

実装してみる

実際にちゃんと動くかを確認するため、スプシのデータを読み取るコードを書いてみます。

using UnityEngine;
using System.Collections.Generic;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using System.Threading.Tasks;

public static async Task LoadScenarioData(string spreadsheetId, string sheetName) {
        string credentialPath = "PATH_TO_FILE";    // credentialsのファイルへのパスを入れる
        string[] scopes = { SheetsService.Scope.SpreadsheetsReadonly };

        GoogleCredential credential = GoogleCredential.FromFile(credentialPath).CreateScoped(scopes);

        SheetsService service = new SheetsService(new BaseClientService.Initializer() {
            HttpClientInitializer = credential
        });

        SpreadsheetsResource.ValuesResource.GetRequest request = service.Spreadsheets.Values.Get(spreadsheetId, sheetName);

        ValueRange response = await request.ExecuteAsync();

        IList<IList<object>> values = response.Values;
        if (values != null && values.Count > 0) {
            foreach (var row in values) {
                foreach (var cell in row) {
                    Debug.Log(cell.ToString());
                }
            }
        }
    }

動作確認用にこんな感じのデータをスプシに用意。

そして、実行してみたところ、、、

// spreadsheetId, sheetNameには実際のスプシIDとシート名を入れる
await ScenarioLoader.LoadScenarioData(spreadsheetId, sheetName);

意図したとおりにログが出力されました!やったね!

おわりに

ということで、実装自体はライブラリを使うとめちゃくちゃ簡単にできたのですが、ライブラリを使えるようにするまでが少しだけ苦労しました。

UnityかつVSCodeという環境でNuGetを使うというのが混乱ポイントでしたね。わかる人にとっては全然大変じゃないのかもですが、わからないサイドの人間だったので、、、

こういうとき、.NET Frameworkについて知らなすぎる弊害を感じるので、どこかで抑えておきたいところです。

今回はスプシの値をただ読み取るだけで留めていますが、データをゲーム処理中に使いやすいような形式で出力したいところ。また、現状スプシ読み込みはUnity Editor上から実行する実装にしているので、Credentialsの置き場はそこまで気を遣わなくて良いのですが、ゲーム起動時にシナリオデータを更新したいとなったときは(なりそう)また考えないとなーと思っています。

番外: Unity Editorからボタンぽちーでスプシ読み込みを実行する

スプシ読み込み処理の呼び出しは、Unity Editor上のボタンをぽちーとすると実行されるようにしています。

具体的には、ヘッダー上にToots/Load Scenario Dataというメニューを作り、

そちらを選択すると、ウィンドウが開き、スプシIDとシート名を入力して"Load Scenario Data"ボタンを押すことで実行される仕組みです。

以下のようなコードで実装してます。これだけの記述で実装できちゃうので便利ですね。実はEditorWindowの実装を自分でするのは初めてだったので、想定通りに動かせたときは嬉しかったものです。

using UnityEngine;
using UnityEditor;

public class ScenarioLoaderEditor : EditorWindow {

    private string spreadsheetId = "";
    private string sheetName = "";

    [MenuItem("Tools/Load Scenario Data")]
    public static void ShowWindow() {
        GetWindow(typeof(ScenarioLoaderEditor), false, "Load Scenario Data");
    }

    private async void OnGUI() {
        GUILayout.Label("Google Spreadsheet Settings", EditorStyles.boldLabel);
    
        spreadsheetId = EditorGUILayout.TextField("Spreadsheet ID", spreadsheetId);
        sheetName = EditorGUILayout.TextField("Sheet Name", sheetName);

        if (GUILayout.Button("Load Scenario Data")) {
            await ScenarioLoader.LoadScenarioData(spreadsheetId, sheetName);
        }
    }
}