7080 + 1

ゲームプログラミングの記事を書いてます。

【Unity】スプレッドシートのデータをスクリプトから取得する【OAuth】

久しぶりの更新です。
仕事柄、最近はUnityばかり触ってます。のでUnityの記事です。

ゲームで使う値をエクセルじゃなくてスプレッドシートで管理したかったので、色々調べました。
全体の流れをメモ代わりに書きますが、詳しく触れていないところはわからないところです。

スプレッドシートにアクセスにするには、2つの方法があります。
Google API Keyを使ってアクセスする
OAuth認証をしてアクセストークンをもらい、それでアクセスする

1つ目はすぐに出来ます。APIKeyさえあればアクセス出来ますが、シートの共有をオンにしておく必要があります。
共有するのが嫌な人はOAuth認証を使いましょう。APIKeyのほうは割愛します。

OAuth認証に必要なもの

OAuth認証によるアクセスでは、以下のものが必要になります。
・OAuthクライアントID
・OAuthクライアントシークレット
OAuth認証コード
・アクセストーク
・リフレッシュトーク
スプレッドシートのID

実際にアクセスするのに必要なのはアクセストークンとスプレッドシートのIDのみです。
アクセストークンを取得するのにOAuth認証コードとクライアントシークレットもしくはリフレッシュトークンが必要になり、
OAuth認証コードやリフレッシュトークンを取得するにはOAuthクライアントIDが必要になります。
結構ややこしい。

注意なのは、アクセストークンには有効期限があり、切れるとそのトークンではアクセスできなくなることです。
その時はリフレッシュトークンを使って、アクセストークンを再発行することになります。
リフレッシュトークンに有効期限はない(多分)ので、どこかに保存しておきましょう。

スプレッドシートのIDは、スプレッドシートのURLの一部になります。
https://docs.google.com/spreadsheets/d/[この部分]/edit#gid=0

手順

OAuthのクライアントIDとクライアントシークレットをAPIConsoleから作る

当たり前ですが、Googleアカウントが必要になります。
Google API Consoleとググるとトップにサイトが出てきます。
そこで認証情報→認証情報を作成→OAuthクライアントの作成を選んでください。

f:id:atori708:20170215001707p:plain

アプリケーションの種類を聞かれるので、その他を選んで、名前は適当につけてください。
これでOAuthのクライアントIDとクライアントシークレットを取得できました。

Unityが登場するのはまだ先です。

OAuthのクライアントIDとScopeから、認証コードを取得する

認証コードを取得するには、クライアントIDとScopeが必要になります。
Scopeは、特定のサービスに、どの権限でアクセスするかを教えるものです。
今回はスプレッドシートの読み書きをしたいので、
https://www.googleapis.com/auth/spreadsheets
がScopeになります。

Scopeの種類は以下に書いてあるので、スプレッドシート以外のサービスにもアクセスできます。
OAuth 2.0 Scopes for Google APIs  |  Google Identity Platform  |  Google Developers

コードを取得するには、
https://accounts.google.com/o/oauth2/v2/auth にクエリをたくさんつけてリクエストを投げます。

クエリは、

?scope=https://www.googleapis.com/auth/spreadsheets
&redirect_uri=http://localhost
&response_type=code
&client_id=OAuthクライアントID

になります。
全部ひとつなぎにしてURL欄に入力してください。

URLが正しく入力できていると以下の画面に飛ぶので、許可を選んでください。
もしGoogleのアカウントにログインする画面に飛んだら、OAuthクライアントを作ったアカウントでログインしてください。

f:id:atori708:20170215001833p:plain

許可を押すとredirect_uriで設定した先へ飛んでいきます。
localhostにしたので何もないですが、URLのcode=~以下がOAuthの認証コードになるので保存しておきましょう。

認証コードからアクセストークンとリフレッシュトークンを取得する

ここからUnityでの作業になります。
UnityのWWWを使って、OAuthからスプレッドシートのアクセストークンをもらいましょう。
https://www.googleapis.com/oauth2/v4/token にフォームデータ付きでリクエストを送信します。

WWWForm form = new WWWForm ();
form.AddField ("code", OAuthの認証コード);
form.AddField ("client_id", OAuthクライアントID);
form.AddField ("client_secret", クライアントシークレット);
form.AddField ("redirect_url", "http://localhost");
form.AddField ("grant_type", "authorization_code");
form.AddField ("access_type", "offline");
form.headers.Add ("Content-Type", "application/x-www-form-urlencoded");
Dictionary<string, string> headers = form.headers;
byte[] rawData = form.data;
WWW www = new WWW ("https://www.googleapis.com/oauth2/v4/token", rawData, headers);

レスポンスはwww.textにJSON形式で入って返ってきます。

こんなJSONが返ってきたら成功です。

{
 "access_token": "アクセストークン",
 "token_type": "Bearer",
 "expires_in": 3600,
 "refresh_token": "リフレッシュトークン"
}

expires_inがアクセストークンの有効期限で、スプレッドシートは1時間のようです。
リフレッシュトークンは数に上限があるそうなので、なるべく同じのを使い続けましょう。

Unityからでなくても、curlコマンドを使うと同じことが出来ます。
その方法は他の人が書いてるので割愛。

アクセストークンからスプレッドシートにアクセスする

このシートのデータを取ってきてみましょう。
テイルズオブベルセリアのキャラです。面白かったです。

f:id:atori708:20170215001651p:plain

ここまで来ると簡単で、
https://sheets.googleapis.com/v4/spreadsheets/スプレッドシートのID/values/取得するセルの範囲?AccessToken=アクセストークン
にPOSTすると、セルの中身がJSON形式で返って来ます。

WWW www = new WWW ("https://sheets.googleapis.com/v4/spreadsheets/スプレッドシートのID/values/取得するセルの範囲?AccessToken=アクセストークン");	

今回はセルの範囲をA1:B4にしました。

{
  "range": "'シート1'!A1:B4",
  "majorDimension": "ROWS",
  "values": [
    [
      "名前",
      "性別"
    ],
    [
      "ベルベット",
      "女"
    ],
    [
      "ライフィセット",
      "男"
    ],
    [
      "ロクロウ",
      "男"
    ]
  ]
}

無事取ってこれたでしょうか。
後はJSONをMiniJSONとか使って保存すればOKですね!

リフレッシュトークンからアクセストークンを取得する

先程も書きましたが、アクセストークンには有効期限があるので、もし切れていたら、リフレッシュトークンを使って再発行する必要があります。
https://www.googleapis.com/oauth2/v4/tokenにリクエストを飛ばすと取得できます。

WWWForm form = new WWWForm ();
form.AddField ("refresh_token", リフレッシュトークン);
form.AddField ("client_id", OAuthクライアントID);
form.AddField ("client_secret", OAuthクライアントシークレット);
form.AddField ("grant_type, "refresh_token");
Dictionary<string, string> headers = form.headers;
byte[] rawData = form.data;
WWW www = new WWW ("https://www.googleapis.com/oauth2/v4/token", rawData);

これで成功していたら、アクセストークンがJSON形式で返ってきます。
アクセストークン期限が切れていなくても再発行可能のようです。
10個くらい連続で取得してみたのですが、全部使えました。

アクセストークンが有効かどうか調べる

有効かどうか、また有効期限も調べられます。

WWW www = new WWW("https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=アクセストークン");

無効だった場合

{
 "error_description": "Invalid Value"
}

※もともと存在しない適当な文字列入れても同じエラーが返ってきます。

有効だった場合(例)

{
 "azp": "247046462806-7efg42ut1tau4n2ku081gn81nebma2ab.apps.googleusercontent.com",
 "aud": "247046462806-7efg42ut1tau4n2ku081gn81nebma2ab.apps.googleusercontent.com",
 "scope": "[]https://www.googleapis.com/auth/spreadsheets[]",
 "exp": "1485228014",
 "expires_in": "3584",
 "access_type": "offline"
}

Unityの記事と言いましたが、認証の仕方は他でも使えると思います。
間違っていたり、質問や不明点がありましたらコメントでお願いします。