[サーバーレス]UnityとFirebaseでアプリのアップデートをせずに、動作と外観の変更を行う

この記事は、[鹿児島大学生 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/kagoshima)の21日目の記事です。

##概要
以前、クライアントからの要件でサーバーを準備せずにアプリ内の表示を変更したいという要望があったので、その時に行なった対応と、UnityとFirebaseの連携を簡単に解説します。

今回はできるだけシンプルな状態で実装まで行おうと思います。
上手く活用すると、アプリの背景画像の変更を行なったり、広告表示の頻度もデータをみながら即時反映することができます。
またFirebaseとの連携でA/Bテストへの応用も簡単に行うことができます。


##環境
- iMac Catalina 10.15.1
- Unity 2019.3.0f3
- Firebase for Unity 6.8.1

Catalinaだと訳あってUnity2019.2系が上手く動いてくれないのでまだ正式リリースされていない2019.3系で行います。。。
UIが変わったのは個人的には好きです。

##セットアップ
[Firebase](https://firebase.google.com/?hl=ja)は、Googleのプラットフォームでいくつものサービスがありますが
今回はRemote Configを使用していきます。
![スクリーンショット 2019-12-18 21.17.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/7695c49a-5bdb-cd1e-9f3e-f8207764daa2.png)

### Unityのプロジェクト構築
まずはサンプルプロジェクトを作成していきます。
中身はなんでもいいのですが、今回は2DプロジェクトでFirebaseからデータを取得するボタンと取得した文言を表示するテキストを準備します。
こちらのやり方は、Unity ボタンとかUnity Textなどで調べるとすぐにわかるので今回は割愛します。
<img width="341" alt="スクリーンショット 2019-12-18 21.21.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/8374b20f-4e61-7ced-c1d3-1c4336ee61bc.png">

### Firebaseプロジェクトを追加
Firebaseを使用するため、[Firebaseコンソール](https://console.firebase.google.com/?hl=ja)からプロジェクトを追加していきます。
![スクリーンショット 2019-12-18 21.26.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/53a71e13-44b4-1702-2663-773ccefd8441.png)

手順2でのアナリティクスは有効にすることで他のFirebaseのサービスと連携することができるのですが、
今回はできるだけシンプルに考えられるようにしたいので、Offにします。
実際は有効にして使うことがほとんどです。
![スクリーンショット 2019-12-18 21.28.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/78bea249-7aa2-2892-5c23-f376a3549b38.png)

Unityアイコンから構成ファイルの作成を行なっていきます。
![スクリーンショット_2019-12-18_21_30_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/4da46ada-0154-940b-5bfd-56ece0d6551e.png)

Register as iOS appとRegister as Android appにチェックを入れてバンドルIDとパッケージ名を登録していきます。
ここは後から変更ができないので、先にストアの作成からしてバンドルIDとパッケージ名を確定しておいた方がいいかもしれません。
![スクリーンショット 2019-12-18 21.44.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/8d5e1754-8899-3d58-8466-4174d046747c.png)

構成ファイルがダウンロードできるので、指示通りダウンロードしてUnityのAssets直下にDDで追加します。
構成ファイルはAssets直下であればSDKがよしなに読み取ってくれるので任意のフォルダに整理しても大丈夫です。
![スクリーンショット 2019-12-18 21.52.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/fb7b8dca-c73c-9e07-c6be-ce4361930afe.png)

SDKをダウンロードしインポートします。
![スクリーンショット 2019-12-18 21.55.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/0814a183-15b3-e2b9-9dcb-fe03a40de74d.png)

zipを解凍したら`dotnet3`と`dotnet4`のフォルダが作成されます。
Unity 5.0以前では、.NET 3.x フレームワークを使用しているため、dotnet3フォルダのpackageを使用します。
Unity 2017.x以降では.NET 4.x フレームワークを使用できるため.NET 4.xを使用する場合はdotnet4フォルダのpackageを使用します。
FirebaseRemoteConfig.unitypackageをダブルクリックで開いているUnityプロジェクトにインポートできます。
<img width="697" alt="スクリーンショット 2019-12-18 22.00.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/72ca439e-a509-d2e6-7296-09e58f09cf96.png">
<img width="1485" alt="スクリーンショット 2019-12-18 22.08.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/7b26c686-93d8-8d2d-1b45-b23a26612622.png">

インポートまで完了したらセットアップは完了です。


##実装
Android 向け Firebase Unity SDK には Google Play 開発者サービスが必要であり、SDK を使用する前に最新版にしておく必要があります。
アプリケーションの先頭に次のコードを追加します。
SDK で他のメソッドを呼び出す前に Google Play 開発者サービスを確認し、必要であれば、Firebase Unity SDK で必要とされるバージョンに更新するようにします。

###初期化
```c#
    void Start()
    {
        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
        {
            var dependencyStatus = task.Result;
            if (dependencyStatus == Firebase.DependencyStatus.Available) {
                // 使用する準備が整った時に通る
            } else {
                Debug.LogError(String.Format("すべてのFirebase依存関係を解決できませんでした: {0}", dependencyStatus));
            }
        });
    }
```

###デフォルトパラメータ値の設定
Remote Config オブジェクトにデフォルトのパラメータ値を設定しておくと、Firebaseから値を取得できない場合にデフォルト値を使用するようにできます。
これにより、オフラインなどの状況でもアプリがクラッシュしないようにできます。

```c#
      // リモート設定を初期化し、デフォルト値を設定します。
    void InitializeRemoteConfig() {
        Dictionary<string, object> defaults = new Dictionary<string, object>();
        defaults.Add("remote_string_value", "ローカルの文字です。");
        FirebaseRemoteConfig.SetDefaults(defaults);
    }
```

###パラメータ値の取得
Firebaseからデータをフェッチすることでデータを取得できます。

```c#
    //フェッチリクエストを開始します。
    public Task FetchDataAsync() {
      Debug.Log("データを取得しています...");
      System.Threading.Tasks.Task fetchTask = Firebase.RemoteConfig.FirebaseRemoteConfig.FetchAsync(
          TimeSpan.Zero);
      return fetchTask.ContinueWithOnMainThread(FetchComplete);
    }

    void FetchComplete(Task fetchTask) {
      if (fetchTask.IsCanceled) {
          Debug.Log("フェッチはキャンセルされました。");
      } else if (fetchTask.IsFaulted) {
          Debug.Log("フェッチでエラーが発生しました。");
      } else if (fetchTask.IsCompleted) {
          Debug.Log("フェッチが正常に完了しました！");
      }

      var info = Firebase.RemoteConfig.FirebaseRemoteConfig.Info;
      switch (info.LastFetchStatus) {
        case Firebase.RemoteConfig.LastFetchStatus.Success:
          Firebase.RemoteConfig.FirebaseRemoteConfig.ActivateFetched();
          Debug.Log(String.Format("リモートデータがロードされ、準備完了 （最終取得時間{0}",
                                 info.FetchTime));
          break;
        case Firebase.RemoteConfig.LastFetchStatus.Failure:
          switch (info.LastFetchFailureReason) {
            case Firebase.RemoteConfig.FetchFailureReason.Error:
              // 不明な理由でフェッチに失敗しました
              break;
            case Firebase.RemoteConfig.FetchFailureReason.Throttled:
              Debug.Log( info.ThrottledEndTime + "まで取得を抑制");
              break;
          }
          break;
        case Firebase.RemoteConfig.LastFetchStatus.Pending:
          Debug.Log("最新のフェッチ呼び出しはまだ保留中です。");
          break;
      }
    }
```

パラメータ値を使用するには`GetBalue()`関数を使用します。

```c#
var value = Firebase.RemoteConfig.FirebaseRemoteConfig.GetValue("remote_string_value").StringValue;
```

これらを使用して、ボタンを押したらフェッチを行います。
先ほど作成したテキストに状態を表示できるようにしていきます。
Debug.LogをすべてDebugLogに置き換えることで表示を更新できるようにします。

```c#
 //デバッグログテキストフィールドとコンソールにテキストを出力します。
    public void DebugLog(string s)
    {
        uiText.text = s;
    }
```

あとはこれらをUnityに設定していきます。


##動作確認
![33527-pabho.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/c31a9d66-04c2-b6e2-866b-2b5397b2b946.gif)


このままだと、まだデフォルトしか表示されていないので次にFirebase側で更新したいパラメータを設定していきます。
先ほど作成したFirebaseプロジェクトからRemote Configの管理画面を開きます。
![スクリーンショット 2019-12-18 22.58.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/c0b9c752-c5f1-52e8-f48b-7b10768b8291.png)

管理画面から、簡単にパラメータの追加が行えるので
先ほどプログラムで引数に与えた`remote_string_value`のキーで設定していきます。
追加できたら右上の`変更を公開`を押して動作確認してみてください。
![スクリーンショット 2019-12-18 22.59.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413778/62f14c21-e477-c13c-7910-69255e81dd24.png)

## さいごに
とても簡単にサーバーレスで即時にアプリの変更をすることができましたね。

ストアの規約上、なんでも変更していいというわけではないですが
A/Bテストなどではとても力を発揮すると思いますので是非使ってみてください。