げぇむぷろぐらみんぐ

日々の生活で得た知識、経験を書きます

【Unity】FirebaseのRealTimeDatabaseを使ってみた

はじめに

直近でサーバーからのPushの実装が必要な案件があったのですが、サーバーサイドの知見はあまり持っておらずWebSocketを用いてある程度の同時接続に耐えうるように作るのは厳しそうだなと思ったので、 どうにかそこらへんから逃げつつ作れないかなと思い先輩に相談したところFirebaseのRealTimeDatabaseを勧められました。

実際にRealTimeDatabaseを使って実装してみたところ、想像以上に簡単にサーバーサイドの実装を終えることができたので書き残します。 クライアントサイドはUnityなので、今回はC#で記述しますがドキュメントを読んだ感じ他の言語でもほぼ同様なようでした。

RealTimeDatabaseとは

FirebaseのRealTimeDatabaseは公式サイトによると、

NoSQL クラウド データベースでデータの保管と同期を行うことができます。データはすべてのクライアントにわたってリアルタイムで同期され、アプリがオフラインになっても、利用可能な状態を保ちます。

とあるように、リアルタイムに同期可能なデータベースです。

クライアントから直接データベースへの書き込みや読み取りを行うような方法でやり取りを行うため、サーバー側は全く意識することなく実装を行うことができます。

また、お値段も非常に安く、同時接続100までは無料で使用することができます。

firebase.google.com

使い方

Unityプロジェクトに対するFirebaseの導入方法などは公式のサイトをご覧ください。

Unity の Firebase Realtime Database を使ってみる  |  Firebase

今回は、データの取得、更新と、リアルタイムに更新を受け取る方法について説明します。

データの取得

データの取得は、以下のようにGetValueAsyncというメソッドを用いて行うことができます。 今回は、コールバックによって結果を受け取っていて、taskが完了したタイミングで結果をLogとして表示しています。

FirebaseDatabase.DefaultInstance.GetReference("user")
    .GetValueAsync().ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            Debug.LogError("not found users");
        }
        else if (task.IsCompleted)
        {
            DataSnapshot snapShot = task.Result;
            Debug.Log(snapShot.Value);
        }
    });

上記のコードは、以下のようなデータ構成だった場合、user以下すべてのデータを読み取ります。

  • user
    • books
    • animals
    • age

すべてを読み取るのではなく、user以下のある特定のデータを読み取る場合は以下のようにします。

FirebaseDatabase.DefaultInstance.GetReference("user")
    .Child("age")
    .GetValueAsync().ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            Debug.LogError("not found users");
        }
        else if (task.IsCompleted)
        {
            DataSnapshot snapShot = task.Result;
            Debug.Log(Convert.ToInt32(snapShot.Value)); // user's age
        }
    });

このように、読み取りたいところまでChildで指定して行ってGetValueAsyncを呼べば、読み取りたいデータのみを読み取ることができます。 また、上記のコードを見るとわかるように、結果はすべてobject型で返されるため適宜変換が必要となるので注意してください。

データの書き込み

データの書き込みに関しても、読み取りとほぼ同様に書き込みたいところまでChildで指定していき、SetValueAsyncによって書き込みを行います。 以下に例を示します。

FirebaseDatabase.DefaultInstance.GetReference("users")
    .Child("age")
    .SetValueAsync(21);

こちらは、基本的には特に型変換もすることなく値を設定することができます。 すでにデータが存在していた場合は上書きされ、存在していなかった場合は新しく子要素として作られます。

データの更新通知

今回一番欲しかった機能である更新検知を行う方法について説明します。 仕組みとしては、ある値が変わったときに呼ばれるメソッドを登録しておくことができます。 以下のように記述することで、メソッドを登録できます。

FirebaseDatabase.DefaultInstance.GetReference("users")
    .Child("age").ValueChanged += HandleAgeValueChanged;

private void HandleAgeValueChanged(object sender, ValueChangedEventArgs args)
{
    if (args.DatabaseError != null)
    {
        Debug.LogError(args.DatabaseError.Message);
        return;
    }


    Debug.Log(args.Snapshot.Value); // current age
}

登録されたメソッドの内部では、データ取得のときとほぼ同様に、SnapShotから現在の値を取得しています。 どこかでageに対して更新処理があった際には、自動的に上記のメソッドが呼ばれることになります。

まとめ

今回は、FirebaseのRealTimeDatabaseを用いてデータの取得、更新、更新通知を行う方法についてまとめました。 その他にもトランザクションとしてデータの書き込みを行うこともできたり、以下の記事のようにリレーションを実現することもできるようです。

qiita.com

まだ実運用していないので、同時接続などの性能面での不安が残っているのですが、ここらへんに関しても学びがあったらまたまとめたいと思います。