Unityとスプレッドシートを用いてスコア登録をしよう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676764/2d48299d-43cc-34c4-a77b-02c0145db033.png)

## 流れ
スコアの送信はUnity側からGAS(Google Apps Script)にデータを送り
GASがデータを成型してスプレッドシートに書き込む。

成型といっても先頭に現在時刻を足し、送られてきたデータ順に列を埋めるだけ。
あとハイスコア、つまり送られたデータが二行目のスコア列より高ければ二行目を上書きする。

ハイスコアの受信はその二行目のデータを持ってくるだけで良い。

この流れを楽にするため、送受信するデータの構造体は
String(CSV形式)→構造体と構造体→String(CSV形式)の関数を作っておく。

## Unity

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

/// <summary>
/// スコアデータの管理とGoogleスプレッドシートとの連携を行うクラス
/// </summary>
public class ScoreDataManager : MonoBehaviour
{
    private ScoreData currentScoreData;

    /// <summary>
    /// スコアデータを格納する構造体
    /// </summary>
    public struct ScoreData
    {
        public string name;
        public int score;
        public int shotCount;
        public List<string> targetHitRate;

        public ScoreData(string name, int score, int shotCount, List<string> targetHitRate)
        {
            this.name = name;
            this.score = score;
            this.shotCount = shotCount;
            this.targetHitRate = targetHitRate;
        }

        /// <summary>
        /// CSVデータからスコアデータを生成
        /// </summary>
        public ScoreData(string data)
        {
            data = data.Replace("\"", "");
            var parts = data.Split(',');
            name = parts[1];
            score = int.Parse(parts[2]);
            shotCount = int.Parse(parts[3]);
            targetHitRate = parts.Skip(4).ToList();
        }

        /// <summary>
        /// スコアデータをCSV形式の文字列に変換
        /// </summary>
        public override string ToString()
        {
            return $"{name},{score},{shotCount},{string.Join(",", targetHitRate)}";
        }
    }

    [SerializeField] private string spreadsheetID = ""; // スプレッドシートのID
    [SerializeField] private string spreadsheetName = ""; // 取得するシート名
    [SerializeField] private string gasUrl = ""; // Google Apps ScriptのURL

    private List<ScoreData> highScoreDataList = new List<ScoreData>();

    /// <summary>
    /// Googleスプレッドシートからスコアデータを取得
    /// </summary>
    private IEnumerator GetData()
    {
        highScoreDataList.Clear();
        string url = $"https://docs.google.com/spreadsheets/d/{spreadsheetID}/gviz/tq?tqx=out:csv&sheet={spreadsheetName}";

        using (UnityWebRequest req = UnityWebRequest.Get(url))
        {
            yield return req.SendWebRequest();

            if (req.result == UnityWebRequest.Result.Success)
            {
                string[] rows = req.downloadHandler.text.Split(new[] { "\n" }, System.StringSplitOptions.RemoveEmptyEntries);

				// ヘッダー行をスキップして二行目のハイスコア行を取得
                foreach (string row in rows.Skip(1)) 
                    highScoreDataList.Add(new ScoreData(row));
            }
        }
    }
    
    /// <summary>
    /// スコアデータをGoogleスプレッドシートに送信
    /// </summary>
    private IEnumerator PostData()
    {
        WWWForm form = new WWWForm();
        form.AddField("val", currentScoreData.ToString());

        using (UnityWebRequest req = UnityWebRequest.Post(gasUrl, form))
            yield return req.SendWebRequest();
    }
}

```

## GAS
```
function doPost(e) {
  // スクリプトのロックを取得（同時実行を防ぐため）
  var lock = LockService.getScriptLock();
  lock.waitLock(30000); // 最大30秒待機

  try {
    // Googleスプレッドシートを開く
    //https://docs.google.com/spreadsheets/d/ ここを /edit?gid=0#gid=0
    var ss = SpreadsheetApp.openById(ここに);
    var sheet = ss.getSheetByName("シート(ページ)名");

    // POSTされたデータを取得
    // parameter."val" は 自分でUnity側から送った名前
    var val = e.parameter.val.split(","); // カンマ区切りのデータを配列に変換

    // 先頭に現在の日時を追加（記録時刻）
    val.unshift(new Date());

    // スプレッドシートの既存データを取得して最高スコアを保存する
    var data = sheet.getDataRange().getValues(); // 全データ取得
    if (data.length >= 2) { // 念のため2行目が存在するか確認
      var highScore = parseFloat(data[1][スコアが保存される列]); // 2行目のスコア
      var newScore = parseFloat(val[スコアが保存される列]); // 新しいスコア

      if (!isNaN(highScore) && !isNaN(newScore) && newScore > highScore) {
        // 新しいスコアの方が大きければ2行目を上書き
        sheet.getRange(2, 1, 1, val.length).setValues([val]);
      }
    }

    // 新しい行を追加
    sheet.appendRow(val);
    return ContentService.createTextOutput("Success").setMimeType(ContentService.MimeType.TEXT);

  } catch (error) {
    // エラー処理
    Logger.log(error.toString());
    return ContentService.createTextOutput("Error: " + error.toString()).setMimeType(ContentService.MimeType.TEXT);
  } finally {
    // 入力ロック解除
    lock.releaseLock();
  }
}
```