Zanim zaczniesz
Zanim zaczniesz korzystać z Realtime Database, musisz:
zarejestrować projekt w Unity i skonfigurować go pod kątem korzystania z Firebase.
Jeśli Twój projekt w Unity korzysta już z Firebase, jest on już zarejestrowany i skonfigurowany pod kątem Firebase.
Jeśli nie masz projektu w Unity, możesz pobrać przykładową aplikację.
dodać pakiet SDK Firebase Unity (w szczególności
FirebaseDatabase.unitypackage) do projektu w Unity.
Pamiętaj, że dodanie Firebase do projektu w Unity wymaga wykonania zadań zarówno w Firebase konsoli jak i w otwartym projekcie w Unity (np. pobierasz pliki konfiguracyjne Firebase z konsoli, a następnie przenosisz je do projektu w Unity).
Zapisywanie danych
Istnieje 5 metod zapisywania danych w Firebase Realtime Database:
| Metoda | Typowe zastosowania |
|---|---|
SetValueAsync() |
Zapisywanie lub zastępowanie danych w zdefiniowanej ścieżce, np.
users/<user-id>/<username>. |
SetRawJsonValueAsync() |
Zapisywanie lub zastępowanie danych surowym kodem JSON, np.
users/<user-id>/<username>. |
Push() |
Dodawanie do listy danych. Za każdym razem, gdy wywołujesz
Push(), Firebase generuje unikalny klucz, który może być też używany
jako unikalny identyfikator, np.
user-scores/<user-id>/<unique-score-id>. |
UpdateChildrenAsync() |
Aktualizowanie niektórych kluczy w zdefiniowanej ścieżce bez zastępowania wszystkich danych. |
RunTransaction() |
Aktualizowanie złożonych danych, które mogą zostać uszkodzone przez jednoczesne aktualizacje. |
Pobieranie DatabaseReference
Aby zapisywać dane w bazie danych, potrzebujesz instancji 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; } }
Zapisywanie, aktualizowanie i usuwanie danych w odniesieniu
Podstawowe operacje zapisu
W przypadku podstawowych operacji zapisu możesz użyć SetValueAsync() do zapisania danych w określonym odniesieniu, zastępując wszystkie istniejące dane w tej ścieżce. Za pomocą tej metody możesz przekazywać typy odpowiadające dostępnym typom JSON w ten sposób:
stringlongdoubleboolDictionary<string, Object>List<Object>
Jeśli używasz obiektu C# z określonym typem, możesz użyć wbudowanej funkcji JsonUtility.ToJson() do przekonwertowania obiektu na surowy kod JSON i wywołać SetRawJsonValueAsync().
Możesz na przykład mieć klasę User, która wygląda tak:
public class User { public string username; public string email; public User() { } public User(string username, string email) { this.username = username; this.email = email; } }
Użytkownika możesz dodać za pomocą SetRawJsonValueAsync() w ten sposób:
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); }
Użycie SetValueAsync() lub SetRawJsonValueAsync() w ten sposób powoduje zastąpienie danych w określonej lokalizacji, w tym wszystkich węzłów podrzędnych. Możesz jednak zaktualizować element podrzędny bez ponownego zapisywania całego obiektu. Jeśli chcesz zezwolić użytkownikom na aktualizowanie profili, możesz zaktualizować nazwę użytkownika w ten sposób:
mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);
Dołączanie do listy danych
Aby dołączyć dane do listy w aplikacjach wielodostępnych, użyj metody Push().
Metoda Push() generuje unikalny klucz za każdym razem, gdy do określonego odniesienia Firebase dodawany jest nowy element podrzędny. Dzięki używaniu tych automatycznie generowanych kluczy dla każdego nowego elementu na liście kilku klientów może jednocześnie dodawać elementy podrzędne do tej samej lokalizacji bez konfliktów zapisu. Unikalny klucz generowany przez Push() jest oparty na sygnaturze czasowej, więc elementy listy są automatycznie porządkowane chronologicznie.
Możesz użyć odniesienia do nowych danych zwróconego przez metodę Push(), aby uzyskać wartość automatycznie wygenerowanego klucza elementu podrzędnego lub ustawić dane dla elementu podrzędnego. Wywołanie Key w odniesieniu Push() zwraca wartość automatycznie wygenerowanego klucza.
Aktualizowanie określonych pól
Aby jednocześnie zapisywać dane w określonych elementach podrzędnych węzła bez zastępowania innych węzłów podrzędnych, użyj metody UpdateChildrenAsync().
Podczas wywoływania UpdateChildrenAsync(), możesz aktualizować wartości elementów podrzędnych niższego poziomu, określając ścieżkę do klucza. Jeśli dane są przechowywane w kilku lokalizacjach, aby zapewnić lepszą skalowalność
, możesz zaktualizować wszystkie instancje tych danych za pomocą
zwielokrotnienia wyjściowego danych. Na przykład gra może mieć klasę LeaderboardEntry w tym stylu:
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; } }
Aby utworzyć LeaderboardEntry i jednocześnie zaktualizować go w kanale najnowszych wyników oraz na liście wyników użytkownika, gra używa kodu takiego jak ten:
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); }
W tym przykładzie użyto Push() do utworzenia wpisu w węźle zawierającym wpisy wszystkich użytkowników w /scores/$key i jednocześnie pobrania klucza za pomocą Key. Klucz może być następnie użyty do utworzenia drugiego wpisu w wynikach użytkownika w /user-scores/$userid/$key.
Za pomocą tych ścieżek możesz jednocześnie aktualizować wiele lokalizacji w drzewie JSON za pomocą jednego wywołania UpdateChildrenAsync(), tak jak w tym przykładzie tworzony jest nowy wpis w obu lokalizacjach. Jednoczesne aktualizacje dokonywane w ten sposób są niepodzielne: albo wszystkie aktualizacje się powiodą, albo wszystkie się nie powiodą.
Usuwanie danych
Najprostszym sposobem usunięcia danych jest wywołanie RemoveValue() w odniesieniu do lokalizacji tych danych.
Możesz też usunąć dane, określając null jako wartość innej operacji zapisu, np. SetValueAsync() lub UpdateChildrenAsync(). Możesz użyć tej techniki z UpdateChildrenAsync(), aby usunąć kilka elementów podrzędnych za pomocą jednego wywołania interfejsu API.
Dowiedz się, kiedy Twoje dane zostaną zatwierdzone.
Aby dowiedzieć się, kiedy Twoje dane zostaną zatwierdzone na serwerze Firebase Realtime Database, możesz
dodać kontynuację. Zarówno SetValueAsync(), jak i UpdateChildrenAsync() zwracają Task, który pozwala dowiedzieć się, kiedy operacja zostanie zakończona. Jeśli wywołanie nie powiedzie się z jakiegokolwiek powodu, IsFaulted w zadaniach będzie mieć wartość true, a właściwość Exception będzie wskazywać przyczynę niepowodzenia.
Zapisywanie danych jako transakcji
Podczas pracy z danymi, które mogą zostać uszkodzone przez jednoczesne
modyfikacje, np. liczniki przyrostowe, możesz użyć
operacji transakcji.
W tej operacji podajesz Func. Ta funkcja aktualizacji Func przyjmuje bieżący stan danych jako argument i zwraca nowy żądany stan, który chcesz zapisać. Jeśli inny klient zapisze dane w tej lokalizacji, zanim nowa wartość zostanie zapisana, funkcja aktualizacji zostanie ponownie wywołana z nową bieżącą wartością, a zapis zostanie ponowiony.
Na przykład w grze możesz zezwolić użytkownikom na aktualizowanie tabeli wyników za pomocą 5 najwyższych wyników:
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); }); }
Użycie transakcji zapobiega nieprawidłowościom w tabeli wyników, jeśli kilku użytkowników zapisuje wyniki w tym samym czasie lub klient ma nieaktualne dane. Jeśli transakcja zostanie odrzucona, serwer zwróci bieżącą wartość do klienta, który ponownie uruchomi transakcję z zaktualizowaną wartością. Powtarza się to, dopóki transakcja nie zostanie zaakceptowana lub nie zostanie wykonana zbyt duża liczba prób.
Zapisywanie danych w trybie offline
Jeśli klient utraci połączenie z siecią, Twoja aplikacja będzie nadal działać prawidłowo.
Każdy klient połączony z bazą danych Firebase utrzymuje własną wewnętrzną wersję aktywnych danych. Gdy dane są zapisywane, najpierw są zapisywane w tej lokalnej wersji. Klient Firebase synchronizuje te dane z serwerami zdalnej bazy danych i innymi klientami na zasadzie „najlepszych starań”.
Dzięki temu wszystkie zapisy w bazie danych natychmiast wywołują zdarzenia lokalne, zanim jakiekolwiek dane zostaną zapisane na serwerze. Oznacza to, że Twoja aplikacja pozostaje responsywna niezależnie od opóźnienia sieci lub połączenia.
Po ponownym nawiązaniu połączenia aplikacja otrzymuje odpowiedni zestaw zdarzeń, dzięki czemu klient synchronizuje się z bieżącym stanem serwera bez konieczności pisania kodu niestandardowego.