Unityでデータベースと連携させたい！（Firebase編）

# Unityをデータベースと連携させたいなと思い、とりあえずFirebaseを使用してScoreデータを取得、更新し、Scoreの値によってSphereの色が変わるサンプルコンテンツを作成しました！

![forqiiita3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/b6a71cfd-79a3-1532-8077-9663995072fc.png)


## 環境
PC: MacBook Air
debug: iPhone8 Plus

## 参照記事
[Unity プロジェクトに Firebase を追加する](https://firebase.google.com/docs/unity/setup?hl=ja)

[Unity の Firebase Realtime Database を使ってみる](https://firebase.google.com/docs/database/unity/start?hl=ja)

[How to use Firebase Database in Unity (REST API)](https://www.youtube.com/watch?v=Fz0Sl4tW5O0)

[【Unity】スクリプトからGameObjectの色を変更する](https://qiita.com/Hal_mai/items/1469398078e81bc7ef27)

[【Unity】GetComponentの使い方と注意点](https://creive.me/archives/16164/)

[Can't compile: BadImageFormatException
](https://forum.unity.com/threads/cant-compile-badimageformatexception.212371/)

[iOS10とXcode8でプロビジョニングファイルを求められた](https://galakutaapp.blogspot.com/2016/09/ios10xcode8.html)

---

:baby_tone2:以下作業手順:baby_tone2:

### その１
#### Unityプロジェクトを作成し、バンドルID を取得

まずは適当にUnityプロジェクト(3D)を準備します

- 今回は最新正式リリースの2019.2.1f1を使用して新規プロジェクトを作成

- iOSビルドは追加していなかったので、モジュール追加も実施

作成できたら、プロジェクトをクリックしてUnityで開き、プラットフォーム固有の ID を取得します

- [File]->[Build Settings]でiOSプラットフォームを選択し、左下の[Player Settings...]を押下

- [Player]タブの右下[Other Settings]をクリック

![スクリーンショット 2019-08-21 13.17.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/3c7670ac-f855-ee4a-2ea5-eaefd5b183f9.png)

- [identification]欄の[Bundle Identifier]に記載されているバンドルID（cf: `com.DefaultCompany.sample01`）をコピーしておく
 

### その２
#### Firebaseにプロジェクト追加

次に、Unityのデータベースとして使うFirebaseプロジェクトを作成します

- Firebaseにプロジェクトを作成（今回は[forUnity]という名前で作成）

### その３
#### FirebaseプロジェクトとUnityプロジェクトを連携

作成したFirebaseプロジェクトに、UnityプロジェクトのバンドルIDを登録します

- Firebaseのコンソール画面に入り、先ほど作成したFirebaseプロジェクト[forUnity]を選択し、「開始するにはアプリを〜」の上に表示されているUnityアイコンをクリック
（もし、画面が違う場合、左上のメニューバーから[Project Overview]をクリックしてUnityアイコン表示画面に移動）

![スクリーンショット 2019-08-21 11.19.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/79f0c041-79fe-2e8d-26ee-2cd05dd72305.png)

- [Register as iOS app]にチェックを入れ、先ほどUnityでコピーしておいたバンドルIDを[iOS バンドルID]の欄に貼り付け

![スクリーンショット 2019-08-21 13.25.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/77ec6d46-4b1e-5f4b-7f22-4ab42811e53c.png)

- 適当にアプリのニックネームを入力(省略可だけど今回は[サンプルアプリ]と入力)し、 [アプリを登録]をクリック

- [GoogleService-Info.plist をダウンロード]をクリックし、保存先を今回作成したUnityプロジェクトのAssets配下の任意の場所（今回はAssets配下にFirebaseというフォルダを作成してその中）に保存し、[次へ]をクリック

- [Firebase Unity SDK をダウンロード] をクリックし、ダウンロードしたZIPファイルを適切な場所で解凍（プロジェクト固有のものじゃないので、今回はSSDのアセットストアのデータを保存しているディレクトリと同階層に解凍）

- 開いているUnityプロジェクトに行き、 [Assets]->[Import Package]->[Custom Package] をクリック、[dotnet4]->[FirebaseDatabase.unitypackage]を[open]


![スクリーンショット 2019-08-21 14.19.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/de2303a0-8d96-4ba5-c340-3245ce748321.png)


- [Import Unity Package] ウィンドウが出てくるので[import]をクリック

- Firebaseに戻り、[次へ]をクリックで FirebaseプロジェクトとUnityプロジェクトの連携完了なので、[Firebaseコンソールに進む]をクリック


### その４
#### Firebaseでデータベースを準備

FirebaseでRealtime Databaseを作成します

- Firebaseコンソール画面左側メニューの[Database]をクリック

- 少しスクロールして、 [または Realtime Database を選択] の[データベースの作成]->[テストモードで開始]（※）->[有効にする]

※今回は簡単に連携させてみたいだけなのでテストモードで作成しますprz

![スクリーンショット 2019-08-21 14.30.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/45b65638-ecf9-fa41-b8de-3357a1eac6cf.png)


### その５
#### Unity側でサンプルコンテンツを準備

UnityプロジェクトにFirebaseのデータベースと連携させるサンプルコンテンツを作成します

- 　[Create]->[UI] から、Text,Input Field,Buttonの３つを作成(w160,h30)し適当に横並びにする
- 　TextはTextを「Score:」,Input FieldはPlaceholderを「Your Name」,ButtonはTextを「Submit」に変更
- 　[Create]->[Create Empty]でGameObjectを作成し、[Add Component]から、「PlayerScores」と検索バーに入力し、[NewScript]->[Create and Add]をクリックし、PlayerScores　という名前のスクリプトをGameObjectに作成&追加

![スクリーンショット 2019-08-21 15.14.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/16c91102-eeb6-a2d2-3829-00a94ef49e96.png)


- 作成した`PlayerScores.cs`を編集



```PlayerScores.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlayerScores : MonoBehaviour
{
    public Text scoreText;
    public InputField nameText;

    private System.Random random = new System.Random();

    public static int playerScore;
    public static string playerName;

    // Start is called before the first frame update
    void Start()
    {
        playerScore = random.Next(0, 101);
        scoreText.text = "Score: " + playerScore;
    }

    public void OnSubmit(){
        playerName = nameText.text;
    }
}
```
- ButtonのOnClick()にGameObjectをアサインし、PlayerScoresのOnSubmit()ファンクションを選択

![スクリーンショット 2019-08-21 15.58.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/646d0c5e-eb6d-3be5-f809-90c5cd6f0752.png)

- 次に、REST APIを投げられるように、Rest Client for Unity をAssetStore で検索し　[Download] and [Import]

![スクリーンショット 2019-08-21 16.12.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/bd402102-35af-eeb3-7dc1-eda07e963755.png)

- importまで完了したら、ProjectフォルダのAssetsのなかで、右クリックし、[Create]->[C# Script]で`User.cs`を作成し、下記のように編集

```User.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public class User
{
    public string userName;
    public int userScore;

    public User(){
        userName = PlayerScores.playerName;
        userScore = PlayerScores.playerScore;
    }
}
```

- `PlayerScores.cs`も下記の箇所を編集


```PlayerScores.cs
    public void OnSubmit(){
        playerName = nameText.text;
    }
```

↓ PostToDatabase()メソッドを追加し、OnSubmit()にも追記

```PlayerScores.cs
    public void OnSubmit(){
        playerName = nameText.text;
        PostToDatabase();
    }

    private void PostToDatabase(){
        User user = new User();
        RestClient.Post("https://forunity-e2ac2.firebaseio.com/.json",user);
    }
```

- スクリプトの追加と修正が終わったら、GameObjectのPlayerScoresスクリプトのフィールドの部分に、Score TextにはScoreのTextオブジェクトを、Name TextにはInputFieldオブジェクトをそれぞれアサイン

![スクリーンショット 2019-08-21 16.34.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/408048ea-16dd-ad00-624f-6793be8e3ee5.png)

- アサインできたら、Unityプロジェクトを実行し、Name欄に名前を入れて、Submitをクリックし、Firebaseのデータベースに値が入れば登録は成功

![スクリーンショット 2019-08-21 16.45.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/3a6c41d6-c518-ba4e-fa98-d19eaa753f7b.png)

- 次に、firebaseからデータを取得するために、Buttonを複製し、Textを「Get Score」に変更

![スクリーンショット 2019-08-21 17.48.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/d9fd027d-13f6-d595-e198-85b497f2ef31.png)

- `PlayerScores.cs`のPlayerScoresクラス配下にOnGetScore(),UpdateScore(),RetrieveFromDatabase()メソッドを追加し、
User user = new User();を上の方で宣言


```PlayerScores（修正後）.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Proyecto26;

public class PlayerScores : MonoBehaviour
{
    public Text scoreText;
    public InputField nameText;

    private System.Random random = new System.Random();

    User user = new User();

    public static int playerScore;
    public static string playerName;

    // Start is called before the first frame update
    void Start()
    {
        playerScore = random.Next(0, 101);
        scoreText.text = "Score: " + playerScore;
    }

    public void OnSubmit(){
        playerName = nameText.text;
        PostToDatabase();
    }

    public void OnGetScore(){
        RetrieveFromDatabase();
    }

    private void UpdateScore(){
        scoreText.text = "Score: " + user.userScore;
    }

    private void PostToDatabase(){
        User user = new User();
        //PostではなくPutに変更し、playerNameの配下にデータが入るようにした
        RestClient.Put("https://forunity-e2ac2.firebaseio.com/"+ playerName +".json",user);
    }

    private void RetrieveFromDatabase(){
        RestClient.Get<User>("https://forunity-e2ac2.firebaseio.com/"+ nameText.text +".json").Then(response => 
        {
            user = response;
            UpdateScore();
        });
    }
}

```


- Name欄に登録ずみのNameを入力し、[Get Store]でScoreの表示されている値が登録済みのものにさし変わったら、データの取得も完了！

![スクリーンショット 2019-08-21 17.48.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/0e1339a9-312b-985b-615f-550900e31307.png)



最後に、Sphereを作成してscore値によって色を変化させる


- `PlayerScores.cs`のPlayerScoresクラス配下にChangeColorName()メソッドを追加し、public GameObject targetObject;を上の方で宣言し、UpdateScore(),PostToDatabase()に、引数にscoreを持たせてChangeColorName()を追記

```PlayerScores（修正後）.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Proyecto26;

public class PlayerScores : MonoBehaviour
{
    public Text scoreText;
    public InputField nameText;

    private System.Random random = new System.Random();

    User user = new User();

    public static int playerScore;
    public static string playerName;

    public GameObject targetObject;

    // Start is called before the first frame update
    void Start()
    {
        playerScore = random.Next(0, 101);
        scoreText.text = "Score: " + playerScore;
    }

    public void OnSubmit(){
        playerName = nameText.text;
        PostToDatabase();
    }

    public void OnGetScore(){
        RetrieveFromDatabase();
    }

    private void UpdateScore(){
        scoreText.text = "Score: " + user.userScore;
        ChangeColorName(user.userScore);
    }

    private void PostToDatabase(){
        User user = new User();
        RestClient.Put("https://forunity-e2ac2.firebaseio.com/"+ playerName +".json",user);
        ChangeColorName(user.userScore);
    }

    private void RetrieveFromDatabase(){
        RestClient.Get<User>("https://forunity-e2ac2.firebaseio.com/"+ nameText.text +".json").Then(response => 
        {
            user = response;
            UpdateScore();
        });
    }

    private void ChangeColorName(int score){
        //Debug.Log(score);
        if(score < 25){
            // red
            targetObject.GetComponent<Renderer>().material.color = Color.red;
        } else if (score < 50){
            // yellow
            targetObject.GetComponent<Renderer>().material.color = Color.yellow;
        } else if (score < 75){
            // green
            targetObject.GetComponent<Renderer>().material.color = Color.green;
        } else {
            // blue
            targetObject.GetComponent<Renderer>().material.color = Color.blue;
        }
        
    }
}

```

- [Create]->[3D Object]->[Sphere]でSphereを作成し、[GameObject]の[Target Object]にアサイン

![スクリーンショット 2019-08-21 19.45.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/b8b6161f-74a7-19bc-324e-140689aec32e.png)

- Sphereの色がScore値(0~100)によって下記仕様で色が変化すればOK

```
0~24: red
25~49: yellow
50~74: green
75~100: blue
```
- まず、確認しやすくするためFirebaseのデータベースに入っているテストデータをいったん削除

- 次に、Unityプロジェクトを実行し、Name欄に「Nobunaga」を入力して[Submit]し、Scoreに合った色であることを確認し実行解除、再度Unityプロジェクトを実行し、今度はName欄に「Ieyasu」を入力して[Submit]し、Scoreに合った色であることを確認

- 確認できたら、Name欄に「Nobunaga」を入力して[Get Score]、正しいScoreと色であることを確認し、続けてName欄に「Ieyasu」を入力して[Get Score]、正しいScoreと色であることも確認できたらサンプルコンテンツ完成

![スクリーンショット 2019-08-21 19.57.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/77b33da6-e0a2-adc5-06ac-5cd16858050d.png)


### 確認作業
#### iOSでビルドしてiPhone実機で確認

完成したUnityプロジェクトをiOS用にビルドして、実機で動作確認する

- まずは [File]->[Build Settings]からiOSを選択して[Switch Platform]、完了したら、Sceneが追加されてなかったら、[Add Open Scene]でSceneを追加してから、[Build And Run]をクリック

![スクリーンショット 2019-08-21 20.10.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/acc5a551-5f65-d3dd-82f8-66f70ab14a46.png)

- 保存場所を聞かれるのでSSDのUnityプロジェクトのディレクトリ配下に適当な名前をつけて[Save]するとビルドが始まる

![スクリーンショット 2019-08-21 20.19.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/b90d6531-fd00-a06b-ae94-8f2730a5216c.png)

...エラー:scream_cat:

##### BadImageFormatException: Format of the executable (.exe) or library (.dll) is invalid.

調べてみたところ、どうやら保存先が外付けなのがよくない模様。。
試しに、Save先をPC側にして再度実行してみた、、が、同様のエラーに。。

なので今度はPC側に今回作成したUnityプロジェクトを丸っとコピーして、それをプロジェクトリストに追加してUnityエディタで開き、Unity上で問題なく動作することを確認してから、Save先をPC側にして再度実行

そしたら、Xcodeまでは無事行けたけれど、別のエラー

##### error: "***Target名***" requires a provisioning profile. Select a provisioning profile for the "Debug" build configuration in the project editor. (in target '***Target名***')

これについては、Targetの[General]タブの[Signing]の[Automatically]にチェックを入れて、適切なTeamを選択してから、再度実行（▶︎）マークを押したら、無事にiPhone実機で確認できました！

![IMG_F5A40749D0BA-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463891/3f6c0a42-f97b-e3f5-b438-1101622c9652.jpeg)

（※Sphereの位置がボタンとかよりも前過ぎたので、Unityで調節し直してから再度ビルドしたのですが、そこは本題ではないので割愛しますprz）

---
##### :alien:振り返り:alien:
またしてもSSD運用のデメリットが見つかってしまい悲しいです。。
とりあえず、 ビルドの段階になったら、

```
Unityプロジェクトを丸っとPC側にコピーして、プロジェクトリストにPC側にコピーしたものを追加してUnityエディタで開く
↓
BuildのSave先もPC側を指定して、Build And Runして実機確認
↓
諸々デバッグ・調整完了したら、PC側の修正を丸っとSSD側にコピーして反映
```

という運用で頑張ろうと思います。

コンテンツ自体はとても簡単なものではありますが、REST APIの部分でAssetを使ったこともあり、想像していたよりあっさり実装できたなと感じました。
~~今度は Rest Client for Unity を使わずに UnityWebRequest を使って実装してみたいです！~~

~~以上！~~

→　UnityWebRequest版　実装しました！ [【続】Unityでデータベースと連携させたい！（Firebase編）](https://qiita.com/kana_omori/items/df22010a1439ae604739)

以上！