kintoneに登録されたデータからUnityのゲームオブジェクトを生成する方法

この記事は [kintone Advent Calendar 2019](https://qiita.com/advent-calendar/2019/kintone) の11日目です。

# Unity x kintone の可能性
11月のCybozu Days 2019のkintone Hack Nightにて、VR出社を実現するためにkintone内の情報をVR世界で表現するデモを披露しました。内容としては下記の記事から確認ができます：

- [[VR出社] VR世界内からチームと一緒に働けるかのか？](https://qiita.com/will-yama/items/8e77a12a9dc65ce4398b)

今回の記事では視点を変え、ゲーム開発にkintoneが関われるか？といったところを見ていきたいと思います。

# やること

kintoneアプリに登録したゲームオブジェクトの情報をVR世界内に反映させます。

1レコード1オブジェクトの情報を保持しており:

- オブジェクトの種類
- オブジェクトのサイズ
- オブジェクトの初期生成位置
- オブジェクトの初期回転値

の情報が1レコードに入っています。

要するに、kintone側で『高い位置にいる逆さまのでっかいドラゴン』の情報を登録すれば、VR世界内でも逆さまのでっかいドラゴンが高い位置で生成されます。

![kintoneunity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/4562ed3e-8a41-5a35-c2de-1aa7482490d0.png)


# 事前準備

この記事ではUnity、kintoneの開発者ライセンスとSimpleJSONを使用します。

## Unity
この記事では Unity 2019.2.7.f2 を利用しました。
Unity を手に入れるには公式サイトからUnity Hubをインストールし、Unity Hub上で上記のバージョンのUnity環境を作成してください。

## kintone開発者ライセンス
無料の開発者ライセンスを[こちらのページ](https://developer.cybozu.io/hc/ja/articles/200720464)から入手することができます。

## SimpleJSON
SimpleJSONはJSONオブジェクトの扱いがしやすくなるためのフレームワークです。
下記の記事を参考にUnity内に設定をしてください：

- [UnityからkintoneのREST APIを叩く方法](https://qiita.com/will-yama/items/19078a905ef4207977e4)

# 実装

## kintone
### アプリのフォーム
新しいアプリを作成し、下記のようなフィールドを配置します。
![kintonefields.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/a7137f28-dcfe-2bf2-9da7-8e190dcb94f3.png)

### アプリのAPIトークン
[この記事](https://developer.cybozu.io/hc/ja/articles/202463840)の通り、このアプリへのREST APIの認証に必要なAPIトークンを生成してください。

### アプリの公開
設定が終わったらアプリの設定を保存して公開します。

## Unity

### 地形のAsset
適当に選びましたが、[Tiled Forest](https://assetstore.unity.com/packages/3d/characters/tiled-forest-117084)というイカしてる地形をインストールしてSceneに配置します。
<img width="841" alt="tiled.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/63008d23-e15b-797f-6d86-85a5f14e7ba9.png">

### 動物のAsset
[Free Low Polygon_Animal](https://assetstore.unity.com/packages/3d/characters/animals/free-low-polygon-animal-110679)という、アニメーションのついたオブジェクトをインストールして、中のDragon, CondorとChickenをProject > Resources というフォルダに格納します。
<img width="244" alt="polygonassets.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/68fe73bd-5d8c-35e8-88da-51c253226d44.png">


## スクリプト

下記のC#スクリプトでkintoneから情報を取得して、ゲームオブジェクトを生成します。
Tiled Forestのオブジェクトにスクリプトを付与してください。
**{サブドメイン名}**、**{APIトークン}**、**{アプリID}**は適宜自分の環境に合うように調整します。

```InitializeKintoneAnimals.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using SimpleJSON;

public class InitializeKintoneAnimals : MonoBehaviour
{
    const string domain = "{サブドメイン名}.cybozu.com";
    const string APItoken = "{APIトークン}";

    void Start()
    {
        string app = "{アプリID}";
        string query = System.Uri.EscapeDataString("order by $id desc");
        string fields = System.Uri.EscapeDataString("fields[0]") + "=" + System.Uri.EscapeDataString("生物")
        + "&" + System.Uri.EscapeDataString("fields[1]") + "=" + System.Uri.EscapeDataString("サイズ")
        + "&" + System.Uri.EscapeDataString("fields[2]") + "=" + System.Uri.EscapeDataString("初期位置X")
        + "&" + System.Uri.EscapeDataString("fields[3]") + "=" + System.Uri.EscapeDataString("初期位置Y")
        + "&" + System.Uri.EscapeDataString("fields[4]") + "=" + System.Uri.EscapeDataString("初期位置Z")
        + "&" + System.Uri.EscapeDataString("fields[5]") + "=" + System.Uri.EscapeDataString("初期回転値X")
        + "&" + System.Uri.EscapeDataString("fields[6]") + "=" + System.Uri.EscapeDataString("初期回転値Y")
        + "&" + System.Uri.EscapeDataString("fields[7]") + "=" + System.Uri.EscapeDataString("初期回転値Z");

        StartCoroutine(
            getKintoneRecords(
                (JSONNode JSONresponse) => 
                {
                    HandleAPIresponse(JSONresponse);
                }, 
                app, 
                query, 
                fields
            )
        );
    }

    private IEnumerator getKintoneRecords(System.Action<JSONNode> callBack, string app, string query, string fields)
    {
        //APIリクエストの準備
        string APIparameters = "app=" + app + "&query=" + query + "&" + fields + "&totalCount=true";
        string RequestURL = "https://" + domain + "/k/v1/records.json?" + APIparameters;
        UnityWebRequest request = UnityWebRequest.Get(RequestURL);
        request.SetRequestHeader("X-Cybozu-API-Token", APItoken);

        //APIのリクエストを送信
        yield return request.SendWebRequest();

        //レスポンスをJSONNode型に格納
        string JSONstring = request.downloadHandler.text;
        Debug.Log("JSONstring--> " + JSONstring);
        JSONNode JSONnode = JSON.Parse(JSONstring);

        //JSONNode型のレスポンスをcallBackに渡す
        callBack(JSONnode);
    }

    private void HandleAPIresponse(JSONNode APIresponse)
    {
        for (int i=0; i<APIresponse["totalCount"].AsInt; i++)
        {
            //生物タイプを設定
            string Choice = APIresponse["records"][i]["生物"]["value"].Value;

            //生物サイズを設定
            float Size = APIresponse["records"][i]["サイズ"]["value"].AsFloat;

            //生物初期値を設定
            int InitialPositionX = APIresponse["records"][i]["初期位置X"]["value"].AsInt;
            int InitialPositionY = APIresponse["records"][i]["初期位置Y"]["value"].AsInt;
            int InitialPositionZ = APIresponse["records"][i]["初期位置Z"]["value"].AsInt;
            Vector3 InitialPositionXYZ = new Vector3 (InitialPositionX, InitialPositionY, InitialPositionZ);

            //生物初期回転値を設定
            int InitialRotationX = APIresponse["records"][i]["初期回転値X"]["value"].AsInt;
            int InitialRotationY = APIresponse["records"][i]["初期回転値Y"]["value"].AsInt;
            int InitialRotationZ = APIresponse["records"][i]["初期回転値Z"]["value"].AsInt;
            Quaternion InitialRotationXYZ = Quaternion.Euler(InitialRotationX, InitialRotationY, InitialRotationZ) ; 

            //生物をInstantiate
            GameObject GameObject = (GameObject)Resources.Load(Choice);
            GameObject SpeciesInstant = Instantiate(GameObject, InitialPositionXYZ, InitialRotationXYZ);
            SpeciesInstant.transform.localScale = new Vector3(Size, Size, Size);
        }
    }
}
```

# 動かしてみる

kintoneアプリに適当なデータを入れます。
Unityのシーンをplayしてみます。

![animaltest1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/5af7454d-aee4-d4d0-17f5-f169d8b0074d.gif)

めっちゃ値が適当で、なんかカオスになりました。
とりあえずまずこっちを向いてもらうように、各レコードの回転値を調整してみました。
最終的なkintone側のデータはこんな感じです。

<img width="866" alt="生物データ.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/fdd1c1b6-1945-3131-19e0-2874cef378d0.png">

Unityのシーンをplayします。

![kiri.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/a350444d-e6ef-aa22-cf76-7f5d30b73bf5.gif)

＼キリッ／

ま、まぁ、とりあえずこれで良いでしょう。

これら6つのオブジェクトはkintoneアプリ内の6つのレコードから生成されています。

試しに、ドラゴンオブジェクト一匹のサイズを調整してみたいと思います。
ブラウザ上で、kintoneの『サイズ』フィールドを変更します。

![changedragonsize.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/e795ae3d-df83-336f-617b-d4132136f763.png)

Unityのシーンをまた読み込みなおします。

![changeddragonsize.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/6361af25-2932-9407-f2ad-85e36b73e236.gif)

こんな感じで、kintoneアプリのデータを手動で変更することにより、Unity側でもきちんと反映されることがわかります。

# おまけ

最後にデータをがーーーっといっぱい入れてシーンをplayしてみましょう！

![monsterhouse.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100542/7602ba55-096b-7691-84f9-f19a3cae3fe1.gif)

＼キリッ／　＼キリッ／　＼キリッ／

良い感じのカオス具合が出たので、ここで終わりたいと思います (つ∀-)ﾎｷﾞｬｰｽ