GAS&Unity Googleスプレッドシートの読込・書込

今回はUnityとGoogleのスプレッドシートを連携させて、スプレッドシートの内容を表示させたり、Unity側からスプレッドシートに情報を追記して行く方法を作ってみたいと思います。
ちなみにこの仕組みを実装する経緯は業務用アプリのプロトタイプを作って欲しいと頼まれて実装してみました。
比較的かんたんに実装出来ますので、参考にしてみてください。

スプレッドシートの読み込み

情報を取得してくる

ではまずスプレッドシートの情報を読み込んでみましょう。
新しくスプレッドシート作成してください。
内容は何でも良いのですが今回はテストなので以下のようにしました。
スプレッドシート作成

  • スプレッドシート名:test_UnityPost_1126
  • シート名:test

シート内の情報はとりあえずテストなので後に空っぽにします。

続いてこのスプレッドシートの共有を設定していきます。
流れはこんな感じです

  • 右上の共有ボタンをクリック
  • リンクを知っている全員に変更をクリック
  • ドロップダウンメニューから「閲覧者を選択」

図にするとこんな感じです。

共有ボタンをクリック
スプレッドシートを共有

リンクを知っている全員に変更
リンクを知っている全員

閲覧者を選択
閲覧者を選択

これでスプレッドシートの共有は完了です。
続いてこのスプレッドシートのIDをメモしておきましょう。
URLの以下の部分の文字列がスプレッドシートのIDになります。
https://docs.google.com/spreadsheets/d/【この部分の文字列】/edit#gid=0

ここまで出来たら次にUnity側で準備をしていきます。

読み込んだ情報を表示させる

Unityで新規プロジェクトを立ち上げます。
今回私が使用しているUnityのバージョンは2021.3.22f1です。

プロジェクトが出来ましたら早速必要なUIを配置していきます。
今回はこのような感じになりました。

  • データをやりとりする空のオブジェクト(DataController) ※1
  • 取得したデータを表示するテキスト(ViewDataText)※2
  • 情報を送信するボタン(DataPushButton)※3
  • 情報入力するインプットフィールド1(nameField)※4
  • 情報入力するインプットフィールド2(commentField)※5

UnityのHierarchyとSceneはこのような感じです。
UIのHierarchyとScene

ViewDataTextは下にどんどん取得したデータを表示させていきますので縦にはみ出しても良いようにInspectorのVertical OverflowをOverflowにしておきます。
Text_Overflow

UIを配置しましたので続いてデータを操作するDataControllerというC#スクリプトを作成します。
作成しましたHierarchyにあるDataControllerオブジェクトにアタッチしておきましょう。
DataControllerオブジェクト

DataControllerでスプレッドシートを取得

早速DataControllerの中身を書いていきます。
処理の流れは以下のように考えています。

  • アプリ起動時にデータ取得用のコルーチン呼び出し
  • スプレッドシートIDを利用したURLでデータを取得
  • データが取得出来たらそのデータを整形していく
  • ViewDataTextに情報を表示させる

上記を踏まえて以下のようなコードを作成しました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking; //ネットワーク用のネームスペース


public class DataController : MonoBehaviour
{
    [SerializeField] Text viewText;
    string url = "https://docs.google.com/spreadsheets/d/コピーしておいたスプレッドシートID/gviz/tq?tqx=out:csv&sheet=シート名";
    List datas = new List(); //データ格納用のStgring型のList
    
    void Start()
    {
        StartCoroutine(GetData()); //データ取得用のコルーチン
    }


    IEnumerator GetData()
    {
        using (UnityWebRequest req = UnityWebRequest.Get(url)) //UnityWebRequest型オブジェクト
        {
            yield return req.SendWebRequest(); //URLにリクエストを送る

            if (IsWebRequestSuccessful(req)) //成功した場合
            {
                ParseData(req.downloadHandler.text); //受け取ったデータを整形する関数に情報を渡す
                DisplayText(); //データを表示する
            }
            else                            //失敗した場合
            {
                Debug.Log("error");
            }
        }
    }

    //データを整形する関数
    void ParseData(string csvData)
    {
        string[] rows = csvData.Split(new []{ "\n"},System.StringSplitOptions.RemoveEmptyEntries); //スプレッドシートを1行ずつ配列に格納
        foreach(string row in rows)
        {
            string[] cells = row.Split(new[] { ',' },System.StringSplitOptions.RemoveEmptyEntries);//一行ずつの情報を1セルずつ配列に格納
            foreach(string cell in cells)
            {
                string trimCell = cell.Trim('"'); //セルの文字列からダブルクォーテーションを除去
                if (!string.IsNullOrEmpty(trimCell)) //除去した文字列が空白でなければdatasに追加していく
                {
                    datas.Add(trimCell);
                }
            }
        }
    }
    
    //文字を表示させる関数
    void DisplayText()
    {
        foreach(string data in datas)
        {
            viewText.text += data + "\n";
        }
    }

    //リクエストが成功したかどうか判定する関数
    bool IsWebRequestSuccessful(UnityWebRequest req)
    {
        /*プロトコルエラーとコネクトエラーではない場合はtrueを返す*/
        return req.result != UnityWebRequest.Result.ProtocolError && 
               req.result != UnityWebRequest.Result.ConnectionError;
    }
}

処理の内容はコメントの通りです。
そしてDataControllerにViewDataTextをアタッチしておきます。
アタッチする

では実行してみましょう。
スプシからデータ取得

画像左半分がスプレッドシートで、右半分がUnityエディターです。
無事にダミーデータを取得しViewDataTextに表示させることが出来ました。

スプレッドシートへの書き込み

書き込むまでの流れ

正常にデータの取得が出来ましたので続いてUnityからスプレッドシートに情報を送信して書き込む処理を作っていきましょう。
大まかな流れは以下の通りです。

  • Unityでデータを送信する仕組みを作る
  • Google Apps Scriptを作成する
  • Google Apps Scriptをデプロイする
  • データ送信する

このような感じになります。
Unityで取得したデータをGoogle Apps Scriptという仕組みを使ってスプレッドシートに書き込んでいきます。
では早速やっていきましょう。

Unity側の設定

先程作ったDataControllerスクリプトでInputFieldやButtonをアタッチしたり必要な関数や変数を定義していきます。
コードは以下の様になります。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI; //追加

public class DataController : MonoBehaviour
{
    [SerializeField] Text viewText;
    [SerializeField] Button dataButton; //追加
    [SerializeField] InputField nameField, commentField; //追加
    string url = "https://docs.google.com/spreadsheets/d/シートのID/gviz/tq?tqx=out:csv&sheet=シート名";
    
    //追加 後ほど使う書き込み用URLの変数
    string gasUrl = ""; 
    
    List datas = new List();
    void Start()
    {
        StartCoroutine(GetData());
        
        //追加 ボタンクリック時の挙動
        dataButton.onClick.AddListener(()=> StartCoroutine(PostData()));
    }
    
    /*中略*/

    //追加 情報を送信する関数 今はまだ空っぽ
    IEnumerator PostData()
    {
        yield return null
    }

    /*中略*/
}

では新たに追加したSerializeFieldに必要なものをアタッチしておきましょう。
追加アタッチ

ここまで出来たら次はボタンをクリックしたときにInputFieldに入力された情報を整形しGoogle Apps Scriptに情報を送信するコードを書いていきます。
具体的にはデータ送信URL用の変数宣言と、PostData関数を作っていきます。
以下のような感じになりました。

public class DataController : MonoBehaviour
{
 /*略*/
 
 
    string gasUrl = "";  //追加(最初は空っぽ)
 
 /*略*/
   
    //送信ボタンをクリックされたときの処理
    IEnumerator PostData()
    {
        //WWWForm型のインスタンスを生成
        WWWForm form = new WWWForm();
        
        //それぞれのInputFieldから情報を取得
        string nameText = nameField.text;
        string commentText = commentField.text;
        
        //値が空の場合は処理を中断
        if(string.IsNullOrEmpty(nameText) || string.IsNullOrEmpty(commentText))
        {
            Debug.Log("empty!");
            yield break;
        }
        
        //それぞれの値をカンマ区切りでcombinedText変数に代入
        string combinedText = string.Join(",", nameText, commentText);
        
        //formにPostする情報をvalというキー、値はcombinedTextで追加する
        form.AddField("val", combinedText);
        
        //UnityWebRequestを使ってGoogle Apps Script用URLにform情報をPost送信する
        using (UnityWebRequest req = UnityWebRequest.Post(gasUrl, form))
        {
            //情報を送信
            yield return req.SendWebRequest();
            
            //リクエストが成功したかどうかの判定
            if (IsWebRequestSuccessful(req))
            {
                
                Debug.Log("success");
            }
            else
            {
                Debug.Log("error");
            }
        }
    }
    
    /*略*/

gasUrlという変数は後にスプレッドシート側の設定を行い取得しますので一旦は空っぽにしてあります。

内容はコメントの通りですが、簡単に流れを説明しますと以下の通りです。

  • WWWFormのインスタンス生成
  • InputFieldの値を取得
  • 上記の値の空白チェック
  • 送信するデータの整形
  • UnityWebRequestを用いてGoogle Apps ScriptのURLに送信

Google Apps Scriptの設定

それではUnityからの情報を受取るApps Scriptを書いていきましょう。
スプレッドシートに移り、機能拡張メニューからApps Scriptを選択するとブラウザ上でエディターが開きます。
機能拡張選択

このような画面が表示されたかと思います。
AppsScriptエディタ1
この中にUnityから送られた情報を受取る処理を書いていきます。
具体的なコードは以下のようになりました。

function doPost(e) {
  /*他のユーザーが書き込み中かどうかの判定*/
  //スクリプトをロックする
  var lock = LockService.getScriptLock();
  while (!lock.tryLock(30000)) { // ロックを取得できない場合は再試行
    Utilities.sleep(1000); // 1秒待つ
  }
  
  /*ロック出来たら処理を行う*/
  try {
    var ss = SpreadsheetApp.openById("スプレッドシートのID"); //スプレッドシートオブジェクトの取得
    var sheet = ss.getSheetByName('シート名'); //書き込む対象のシートオブジェクトの取得
    
    var col = 1; //開始する列の位置
    var val = e.parameter.val.split(","); //Unityから送られた情報をカンマ区切りで分割しvalに代入
    var row = sheet.getLastRow() + 1; //現在シートに書き込まれている最終行に1を追加して変数rowに代入
    
    //シートに書き込む領域を取得した後に送信された情報valを書き込む
    sheet.getRange(row, col, 1, val.length).setValues([val]);
  } finally {
    //ロックを解除する
    lock.releaseLock();
  }
  
}

このコードはJavaScriptにとても似ていますが、Google Apps Script(GAS)というGoogleが提供するスクリプト言語です。
冒頭、今回はあまり必要ないかも知れませんが複数のユーザーが同時に書き込みを行った場合、スプレッドシートに書き込む情報が上書きされてしまう可能性があります。
それを回避するために記述しました。
この書き方が良いのかどうかわかりませんが、他に良い方法があればコメントで教えて頂けると嬉しいです。

次にスプレッドシートのIDを指定し、シート名を指定してそれぞれのオブジェクトを取得しています。
続いてUnityから送られてきたカンマ区切りのデータを分割し、スプレッドシートに追記していく情報に整形しています。
その後データを追加していく開始の列と行をしていし、最後に書き込む範囲を指定して実際にデータを書き込んでいます。
ソースコードは一旦これで実行したいので続いてGoogle Apps Scriptのデプロイを行っていきます。

Google Apps Scriptのデプロイ方法

では先程のコードを実行出来るようにデプロイしていきます。
今回はテスト用のデプロイなので少々わかりにくい箇所があります。なので画像を入れて説明していきます。

右上のデプロイのボタンをクリックし、「新しいデプロイ」を選択します。
新しいデプロイ

続いてデプロイの種類を選択します。今回は「ウェブアプリ」を選択します。
デプロイの種類

続いてアクセス出来るユーザーを「全員」にします。
全員に公開

アクセスを許可」をクリックします。
デプロイ確認

アカウントの選択を聞かれますので使用するアカウントを選択してください。
アカウントの選択

Googleはこのアプリを検証していませんというアラートが表示されます。今回はテストですからそのまま使いたいと思います。
左下のグレーになっている文字「Advanced」をクリックしてください。
Advanced

するとアラートの内容が変わりますので左下の「Go To GASのプロジェクト名(unsafe)」という文字をクリックしてください。私の場合はUnityTestProjectとなっています。
unsafe

続いてGASのプロジェクトがあなたのGoogleアカウントにアクセスしたいと表示されますから「Allow」をクリックします。
Allow

そうすると作成したGoogle Apps ScriptのデプロイされたURLが発行されますのでそれをコピーしましょう。
デプロイURL

ではUnityに戻り、DataControllerスクリプトで空になっていたgasUrlにコピーしたURLを貼り付けます。

ここまで来ましたらちゃんと送信出来るかどうか実験してみましょう。
スプレッドシートのダミーデータを全部削除して、Unityからデータを書き込みんでみます。
Unityからスプレッドシートへ追記

無事にUnityからスプレッドシートに書き込むことが出来ました。

ちなみにGASのデプロイ時に出てくるアラートですが、GooGle Cloud ConsoleでApps Scriptの申請をすれば「Googleがこのアプリを確認していません」という警告を受けること無く使用することができるそうですので、興味のある方はそちらもやってみてください。

アプリ全体の仕上げ

最後にアプリを仕上げて行きましょう。
具体的には以下の2つを実装します。

  • 送信成功後にInputFieldを空白にする
  • 送信後に最新のスプレッドシート情報を表示させる

まずは送信したらそれぞれのInputFieldを空にします。
ResetInputFieldsという関数を作成し、PostDataコルーチンの書き込み成功後に呼び出すことにします。
続いてPostDataコルーチンの最後にGetDataコルーチンを呼び出します。
ここで注意です。
スプレッドシート読み込み時にdatasというString型のListと取得したデータを表示するviewTextを空っぽにする必要があります。
GetDataの冒頭でその処理も入れておきましょう。
最終的には以下の様になりました。


    IEnumerator GetData()
    {
        //追加 listとtextを空にする
        datas.Clear();
        viewText.text = "";
        
        /* 中略 */
    }

    IEnumerator PostData()
    {
        /* 中略 */
        
        using (UnityWebRequest req = UnityWebRequest.Post(gasUrl, form))
        {
            yield return req.SendWebRequest();
            if (IsWebRequestSuccessful(req))
            {
                Debug.Log("success");
                //追加 InputFieldを空にする
                ResetInputFields();
            }
            else
            {
                Debug.Log("error");
            }
        }
        
       //追加 スプレッドシートから情報を取得する
       StartCoroutine(GetData());
    }

    //追加 InputFieldを空にする関数
    void ResetInputFields()
    {
        nameField.text = "";
        commentField.text = "";
    }

それでは実行してみます。
Unityとスプレッドシートの連携完了

このようにデータ送信後の処理もうまく行きました。

以上Unityとスプレッドシートの連携方法でした。
今回の内容をGitHubに公開しておきましたので、よろしければ何かの参考にしてみてください。

関連記事

最後までご覧頂いてありがとうございました。