UnityでGoogleスプレッドシートに書き込む

とりあえず、Unityからスプレッドシートに書き込みが行えたので備忘録として残しておく。

#Unityのバージョン
たぶん.Net 4.xでビルドできるバージョンであれば問題ないと思う。
ちなみに、私が実証したバージョンは2018.2.10f1。OSはWin10。

#Google Sheets API用ライブラリのインストール
Googleのドキュメントにはライブラリのインストール方法がNugetの方法しか載っていないため、Nugetでインストールする。
だが、ここでUnityのPlayer SettingsにおいてScripting Runtime Versionが`.Net 3.5 Equivalent`など4.x未満のバージョンが設定された状態だとインストール時にエラーとなる。
そのためPlayer Settingsを開き、Scripting Runtime Versionを `.Net 4.x Equivalent` に設定する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/17554/11130e82-c3d3-7c96-f60c-97a8c4d4f808.png)
そして、適当なGameObjectを選択し、InspectorでAddComponentボタンをクリックし、新規のC#スクリプトファイルを作成。
(ファイル名は適当でいいのだが、今回はサンプルコードのクラス名を"GoogleSheetsApiTest"としている)
そのC#スクリプトのEditをクリックし、Visual Studioを起動。
Visual Studioでツール→Nugetパッケージマネージャー→パッケージマネージャーコンソールをクリックすると、パッケージマネージャーコンソールが開くので
```PM> Install-Package Google.Apis.Sheets.v4
```を実行しインストールする。

インストールしたら、Visual Studio上ではライブラリが認識されているが、インストールされる場所がAssetsフォルダ配下外であるため、Unityからは認識されない。なので、このまま実行しようとすると"Google"というネームスペースが見つからないといったエラーが発生して実行できない。
Unityでライブラリを使えるようにするには、Assets/Pluginsフォルダにライブラリ(dll)をコピーする。
ライブラリのインストール先は参照プロパティのパスでわかる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/17554/4ffb5d1d-e554-fed9-eeaf-c0803003ccd1.png)
コピーするdllは以下の７つ
* Google.Apis.Auth.dll
* Google.Apis.Auth.PlatformServices.dll
* Google.Apis.Core.dll
* Google.Apis.dll
* Google.Apis.PlatformServices.dll
* Google.Apis.Sheets.v4.dll
* Newtonsoft.Json.dll

Assets/Pluginsフォルダにコピーしたら、Unity上からでもライブラリが使用できるようになる。

#APIの使用設定およびスプレッドシートを新規作成し、共有設定を行う。
これらの手順は[【もう迷わない】Pythonでスプレッドシートに読み書きする初期設定まとめ](https://tanuhack.com/python/operate-spreadsheet/)に詳しく書かれているのでこの記事を参考にAPIの使用の設定を行い、書き込みサンプル用にスプレッドシートを新規作成し、共有設定を行う。
APIの使用設定において、認証情報のJSONファイルがダウンロードされるので、このJSONファイルをAssets/StreamingAssetsフォルダにコピーまたは移動する。

#サンプルコード
これで準備が整ったので、コードを書いて実行すれば、シートに値が書き込まれる。
コードは[C#.NETでGoogle Sheets API 4を使ってSpreadSheetを読み書きしてみる](https://qiita.com/linquanstudio/items/23fca582ba6ae0d6d328)のサンプルコードをほぼコピペ。
ただ、このサンプルコードは無限ループで一定間隔で書き込むコードとなっているため、1回だけ書き込むように修正。
また、//CredentialファイルがcredPathに保存されるというコメントが書かれたコードでエラーが発生したので、この行も修正したのが以下のコード。
これを適当なGameObjectにアタッチして実行すればシートに書き込まれた。

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System.IO;
using System.Threading;
using System;

public class GoogleSheetsApiTest : MonoBehaviour {

	// Use this for initialization
	void Start () {
        writeSheet();
	}
	
	// Update is called once per frame
	void Update () {
		
	}

    //****************************************************************
    //Scopeで読み取りのみ、読み書きなど指定する。
    //scopeを変更したら、credentialsしなおすため、
    // ~/.credentials/sheets.googleapis.com-dotnet-quickstart.json
    //をいったん削除して再度認証プロセスを通すこと
    //****************************************************************
    static string[] Scopes = { SheetsService.Scope.Spreadsheets };
    static string ApplicationName = "Google Sheets API .NET Quickstart";
    static string spreadsheetId = "1JxF20UTn9bBgSBmGMq9cZs1PBEcCuIlQGaYEMM8XOyA";
    static string sheetName = "hoge";

    //****************************************************************
    //Main - 10秒毎に追加する。終了するときはCtl-Cで。
    //****************************************************************
    void writeSheet()
    {
        var service = OpenSheet();
        ReadWrite(service);
    }

    //****************************************************************
    //OpenSheet() - 認証プロセスとAPI serviceの作成
    //****************************************************************
    static SheetsService OpenSheet()
    {
        GoogleCredential credential;
        //認証プロセス。credPathが作成されていないとBrowserが起動して認証ページが開くので認証を行って先に進む
        using (var stream = new FileStream(Application.streamingAssetsPath + "/SheetsAPI-952e259722ea.json", FileMode.Open, FileAccess.Read))
        {
            //CredentialファイルがcredPathに保存される(ここのコードを以下のように修正)
            credential = GoogleCredential.FromStream(stream).CreateScoped(SheetsService.Scope.Spreadsheets);
        }
        //API serviceを作成、Requestパラメータを設定
        var service = new SheetsService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = ApplicationName,
        });
        return service;
    }

    //****************************************************************
    //OpenSheet() - 本当はAppendしたいんだが動かない(というかエントリがない)ので、
    // 1. 今あるデータを全部読み出す
    // 2. 行数を調べてその次の行にデータを書く
    //****************************************************************
    static void ReadWrite(SheetsService service)
    {
        ValueRange rVR;
        string wRange;
        //データを読み出す
        int rowNumber = 1;
        wRange = string.Format("{0}!A{1}:B", sheetName, rowNumber); //行を全部読む
        SpreadsheetsResource.ValuesResource.GetRequest getRequest
            = service.Spreadsheets.Values.Get(spreadsheetId, wRange);
        rVR = getRequest.Execute();
        var values = rVR.Values;
        if (values != null && values.Count > 0) rowNumber = values.Count + 1;
        //空行に新たにデータを書き込む
        wRange = string.Format("{0}!A{1}:B{1}", sheetName, rowNumber);  //行を追加
        ValueRange valueRange = new ValueRange();
        valueRange.Range = wRange;
        valueRange.MajorDimension = "ROWS";
        DateTime dt = new DateTime();
        dt = DateTime.Now;
        string dts = dt.ToString("HH:mm:ss");
        var oblist = new List<object>() { string.Format("{0}", rowNumber), dts };
        valueRange.Values = new List<IList<object>> { oblist };
        var updateRequest = service.Spreadsheets.Values.Update(valueRange, spreadsheetId, wRange);
        updateRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.USERENTERED;
        var uUVR = updateRequest.Execute();
    }
}
```