あなたが始める前に
Realtime Databaseを使用する前に、次のことを行う必要があります。
Unity プロジェクトを登録し、Firebase を使用するように構成します。
Unity プロジェクトですでに Firebase を使用している場合は、Firebase 用に既に登録および構成されています。
Unity プロジェクトがない場合は、サンプル アプリをダウンロードできます。
Unity プロジェクトにFirebase Unity SDK (具体的には
FirebaseDatabase.unitypackage
) を追加します。
Unity プロジェクトに Firebase を追加するには、 Firebase コンソールと開いている Unity プロジェクトの両方でタスクが必要になることに注意してください (たとえば、コンソールから Firebase 構成ファイルをダウンロードして、それらを Unity プロジェクトに移動します)。
データの保存
Firebase Realtime Database にデータを書き込む方法は 5 つあります。
方法 | 一般的な用途 |
---|---|
SetValueAsync() | users/<user-id>/<username> などの定義済みパスにデータを書き込むか、または置き換えます。 |
SetRawJsonValueAsync() | users/<user-id>/<username> などの生の Json でデータを書き込むか、置き換えます。 |
Push() | データのリストに追加します。 Push() を呼び出すたびに、Firebase はuser-scores/<user-id>/<unique-score-id> などの一意の識別子としても使用できる一意のキーを生成します。 |
UpdateChildrenAsync() | すべてのデータを置き換えることなく、定義されたパスのいくつかのキーを更新します。 |
RunTransaction() | 同時更新によって破損する可能性がある複雑なデータを更新します。 |
DatabaseReference を取得する
データベースにデータを書き込むには、 DatabaseReference
のインスタンスが必要です。
using Firebase; using Firebase.Database; public class MyScript: MonoBehaviour { void Start() { // Get the root reference location of the database. DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference; } }
参照でのデータの書き込み、更新、または削除
基本的な書き込み操作
基本的な書き込み操作では、 SetValueAsync()
を使用して指定した参照にデータを保存し、そのパスにある既存のデータを置き換えることができます。このメソッドを使用して、次のように使用可能な JSON 型に対応する型を渡すことができます。
-
string
-
long
-
double
-
bool
-
Dictionary<string, Object>
-
List<Object>
型指定された C# オブジェクトを使用する場合は、組み込みのJsonUtility.ToJson()
を使用してオブジェクトを生の Json に変換し、 SetRawJsonValueAsync SetRawJsonValueAsync()
) を呼び出すことができます。たとえば、次のような User クラスがあるとします。
public class User { public string username; public string email; public User() { } public User(string username, string email) { this.username = username; this.email = email; } }
次のようにSetRawJsonValueAsync()
を使用してユーザーを追加できます。
private void writeNewUser(string userId, string name, string email) { User user = new User(name, email); string json = JsonUtility.ToJson(user); mDatabaseRef.Child("users").Child(userId).SetRawJsonValueAsync(json); }
このようにSetValueAsync()
またはSetRawJsonValueAsync()
を使用すると、子ノードを含め、指定された場所のデータが上書きされます。ただし、オブジェクト全体を書き換えなくても子を更新できます。ユーザーがプロファイルを更新できるようにしたい場合は、次のようにユーザー名を更新できます。
mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);
データのリストに追加
マルチユーザー アプリケーションのリストにデータを追加するには、 Push()
メソッドを使用します。 Push()
メソッドは、指定された Firebase 参照に新しい子が追加されるたびに一意のキーを生成します。リスト内の新しい要素ごとにこれらの自動生成されたキーを使用することにより、複数のクライアントが、書き込みの競合なしで同時に同じ場所に子を追加できます。 Push()
によって生成される一意のキーはタイムスタンプに基づいているため、リスト項目は自動的に時系列順に並べられます。
Push()
メソッドによって返された新しいデータへの参照を使用して、子の自動生成キーの値を取得したり、子のデータを設定したりできます。 Push()
参照でKey
を呼び出すと、自動生成されたキーの値が返されます。
特定のフィールドを更新する
他の子ノードを上書きせずにノードの特定の子に同時に書き込むには、 UpdateChildrenAsync()
メソッドを使用します。
UpdateChildrenAsync()
を呼び出すときに、キーのパスを指定することで、下位レベルの子の値を更新できます。スケーリングを改善するためにデータが複数の場所に保存されている場合は、データ ファンアウトを使用してそのデータのすべてのインスタンスを更新できます。たとえば、ゲームには次のようなLeaderboardEntry
クラスがあるとします。
public class LeaderboardEntry { public string uid; public int score = 0; public LeaderboardEntry() { } public LeaderboardEntry(string uid, int score) { this.uid = uid; this.score = score; } public Dictionary<string, Object> ToDictionary() { Dictionary<string, Object> result = new Dictionary<string, Object>(); result["uid"] = uid; result["score"] = score; return result; } }
LeaderboardEntry を作成し、それを最近のスコア フィードとユーザー自身のスコア リストに同時に更新するために、ゲームは次のようなコードを使用します。
private void WriteNewScore(string userId, int score) { // Create new entry at /user-scores/$userid/$scoreid and at // /leaderboard/$scoreid simultaneously string key = mDatabase.Child("scores").Push().Key; LeaderBoardEntry entry = new LeaderBoardEntry(userId, score); Dictionary<string, Object> entryValues = entry.ToDictionary(); Dictionary<string, Object> childUpdates = new Dictionary<string, Object>(); childUpdates["/scores/" + key] = entryValues; childUpdates["/user-scores/" + userId + "/" + key] = entryValues; mDatabase.UpdateChildrenAsync(childUpdates); }
この例では、 Push()
を使用して、 /scores/$key
にあるすべてのユーザーのエントリを含むノードにエントリを作成し、同時にKey
でキーを取得します。このキーを使用して、 /user-scores/$userid/$key
でユーザーのスコアに 2 番目のエントリを作成できます。
これらのパスを使用すると、この例で両方の場所に新しいエントリを作成する方法のように、 UpdateChildrenAsync()
を 1 回呼び出すだけで、JSON ツリー内の複数の場所に対して同時に更新を実行できます。この方法で行われる同時更新はアトミックです。すべての更新が成功するか、すべての更新が失敗します。
データを削除する
データを削除する最も簡単な方法は、そのデータの場所への参照に対してRemoveValue()
を呼び出すことです。
SetValueAsync()
やUpdateChildrenAsync()
) などの別の書き込み操作の値としてnull
を指定して削除することもできます。この手法をUpdateChildrenAsync()
で使用して、1 回の API 呼び出しで複数の子を削除できます。
データがいつコミットされるかを把握します。
データが Firebase Realtime Database サーバーにいつコミットされるかを知るために、継続を追加できます。 SetValueAsync()
とUpdateChildrenAsync()
はどちらも、操作がいつ完了したかを知ることができるTask
を返します。何らかの理由で呼び出しが失敗した場合、Tasks IsFaulted
は true になり、失敗が発生した理由を示すException
プロパティが表示されます。
データをトランザクションとして保存
増分カウンターなど、同時変更によって破損する可能性があるデータを操作する場合は、トランザクション操作を使用できます。この操作にFunc
を与えます。この update Func
は、データの現在の状態を引数として取り、書きたい新しい望ましい状態を返します。新しい値が正常に書き込まれる前に別のクライアントがその場所に書き込むと、更新関数が新しい現在の値で再度呼び出され、書き込みが再試行されます。
たとえば、ゲームでは、ユーザーが 5 つの最高スコアでリーダーボードを更新できるようにすることができます。
private void AddScoreToLeaders(string email, long score, DatabaseReference leaderBoardRef) { leaderBoardRef.RunTransaction(mutableData => { List<object> leaders = mutableData.Value as List<object> if (leaders == null) { leaders = new List<object>(); } else if (mutableData.ChildrenCount >= MaxScores) { long minScore = long.MaxValue; object minVal = null; foreach (var child in leaders) { if (!(child is Dictionary<string, object>)) continue; long childScore = (long) ((Dictionary<string, object>)child)["score"]; if (childScore < minScore) { minScore = childScore; minVal = child; } } if (minScore > score) { // The new score is lower than the existing 5 scores, abort. return TransactionResult.Abort(); } // Remove the lowest score. leaders.Remove(minVal); } // Add the new high score. Dictionary<string, object> newScoreMap = new Dictionary<string, object>(); newScoreMap["score"] = score; newScoreMap["email"] = email; leaders.Add(newScoreMap); mutableData.Value = leaders; return TransactionResult.Success(mutableData); }); }
トランザクションを使用すると、複数のユーザーが同時にスコアを記録したり、クライアントのデータが古い場合にリーダーボードが不正確になるのを防ぐことができます。トランザクションが拒否された場合、サーバーは現在の値をクライアントに返し、クライアントは更新された値でトランザクションを再度実行します。これは、トランザクションが受け入れられるか、試行回数が多すぎるまで繰り返されます。
オフラインでデータを書き込む
クライアントがネットワーク接続を失った場合でも、アプリは正常に機能し続けます。
Firebase データベースに接続されているすべてのクライアントは、アクティブなデータの独自の内部バージョンを維持します。データが書き込まれると、最初にこのローカル バージョンに書き込まれます。次に、Firebase クライアントは、そのデータをリモート データベース サーバーおよび他のクライアントと「ベスト エフォート」ベースで同期します。
その結果、データベースへのすべての書き込みは、データがサーバーに書き込まれる前に、すぐにローカル イベントをトリガーします。これは、ネットワークの遅延や接続に関係なく、アプリの応答性が維持されることを意味します。
接続が再確立されると、アプリは適切な一連のイベントを受け取り、クライアントが現在のサーバーの状態と同期するようにします。カスタム コードを記述する必要はありません。