Google Spreadsheetの値をScriptableObjectに反映させる

# はじめに
**Google Spreadsheet**のデータを**ScriptableObject**に反映させるための一例を紹介します。
Google spreadsheetは幅広く利用されており、実装者でなくても使いこなせるのが魅力です。見た目を作り込めば、ちょっとしたWebアプリに見えないこともないです。下のスクリーンショットは、前のプロジェクトで作ったサウンドさん用の作曲ツール(Spreadsheet)です。
![スクリーンショット 2021-12-09 2.19.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95460/2f777963-bfc7-f35b-9161-7599f11f12ea.png)

ScriptableObjectはデータを格納できるオブジェクトです。パラメーターを変えるたびに、実行を止めて最初から始めなくてはならない、変えるたびに処理が走って待たされる、そんな経験があります。
ScriptableObjectはビルド対象外なので**値を変えても処理が走りません。**実行中でも値を変えられて、実行を解除しても値が残ります。レベルデザインなど変更の多い値はなるべくScriptableObjectにまとめたいものです。

前回のアドベントカレンダーの記事は[こちら](https://qiita.com/TakashiHamada/items/660007f341f3537c1ce1)です。
プロジェクトが一区切りしたので、今まで気になっていた部分を、勉強したり実験したりしています。

今年のアドベントカレンダーで扱うサンプルは、すべて同じレポジトリにまとめています。
記事では**すべてのソースコードは掲載していません**ので、全部見たい方はgithubをご参照ください。

https://github.com/TakashiHamada/advent_calendar_2021
# 作成したサンプル
![画面収録 2021-12-09 1.59.31_3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95460/1b8addab-b86a-4352-7141-8b4c7e43bbc1.gif)

## 仕様

![スクリーンショット 2021-12-09 2.48.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95460/f2a17fac-31dc-466b-51af-1f33993f4536.png)

1. Spreadsheetで値を変更する。
2. Unityの更新ボタンを押すと、ScriptableObjectの値が変更される。

- 値は、ゲームが実行中かどうか関わりなく、反映できる。
- ボタンを簡単に表示したり、要素を装飾したりする目的でOdin Inspectorを導入している。

https://assetstore.unity.com/packages/tools/utilities/odin-inspector-and-serializer-89041?locale=ja-JP

# 実装の特徴

## Inspectorのボタン

Odin Inspectorの機能を使用して、ボタンをInspectorに表示します。
この例では、ボタンが一番上に表示されるよう、優先順位を指定しています。
また、目立だつようボタンに色をつけてみました。

```cs
[PropertyOrder(0)] [LabelText("Spreadsheetを反映")]
[Button] [GUIColor(0, 1, 1)]
private void UpdateSpeechScore()
{
    ...
}
```

## Inspectorのログ

進行度合いを知らせるため、ボタンの下にログを表示してみました。
![画面収録 2021-12-09 1.59.31_4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95460/9c179da2-a966-98ad-75c0-8b03890bba31.gif)


```cs
[PropertyOrder(1)][HideLabel] [DisplayAsString] public string message;
```

## 複数のInspectorとLock

このボタンはエディタに常に表示したいので、Inspectorウィンドウを新たに追加し、右上の**Lockアイコン**を押します。
下の画像では２つのInspectorウィンドウが左右に並んでいるのが確認できると思います。
![スクリーンショット 2021-12-09 3.19.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95460/3434b19a-f919-2812-0287-9f33f8162edf.png)

## Google Spreadsheetとの通信

[Google apps script](https://workspace.google.co.jp/intl/ja/products/apps-script/)を使用して、Webアプリを作ります。
作り方の記事はたくさんあるので、ここでは省略します。
![スクリーンショット 2021-12-09 2.09.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95460/c0bd87ea-e9d8-683a-ff91-ae73111c370f.png)

```js
function doGet(e) {
    let out;
    switch (e.parameter.mode) {
        case "test" :
            let ss = SpreadsheetApp.getActive();
            let sheet = ss.getSheetByName("Sheet_0");
            let values = sheet.getRange(2, 1, sheet.getLastRow() - 1, 2).getValues();
            var json = {};
            for (var i = 0; i < values.length; i++) {
                json[values[i][0]] = values[i][1];
            }
            out = ContentService.createTextOutput(JSON.stringify(json, null, '\t'));
            
            break;
        default: break;
    }
    return out;
}
```

spreadsheetのセルの情報を取得、json形式にして、テキストで返します。
[こちら](https://script.google.com/macros/s/AKfycbxXDOS7W9ChPktR3UFh8wf_4Phdadblpjn6Hg9OXwNVB5sSAGZUc9C8uhvB2Ab3JBrg/exec?mode=test)をクリックすると、最新のspreadsheetのデータがjson形式で表示されると思います。
`https://script.google.com/macros/s/AKfycbxXDOS7W9ChPktR3UFh8wf_4Phdadblpjn6Hg9OXwNVB5sSAGZUc9C8uhvB2Ab3JBrg/exec?mode=test`

## jsonの受け取り

受け取るjsonにあわせて、クラスを作ります。

```cs
[Serializable]
class WebData
{
    public string key_0;
    public string key_1;
}
```

JsonUtilityを使って変換します。

```cs
var data = JsonUtility.FromJson<WebData>(json);
```

## ScriptableObjectへの書き込み

データを格納する形を決めます。
これだけでは使えないので、Projectウィンドのメニューからオブジェクトを作ってください。
この例では、`Create/ScriptableObjects/GeneralParameter`で作成できます。

```cs
[CreateAssetMenu(menuName = "ScriptableObjects/GeneralParameter")]
public class GeneralParameter : ScriptableObject
{
    public string param_0;
    public string param_1;
}
```

jsonから変換したdataの値を渡してあげればそれでOK!

```cs
this.generalParameter.param_0 = data.key_0;
this.generalParameter.param_1 = data.key_1;
```