Zanim zaczniesz
Zanim zaczniesz korzystać z Realtime Database, musisz:
Zarejestruj projekt Unity i skonfiguruj go tak, aby używał Firebase.
Jeśli Twój projekt Unity korzysta już z Firebase, jest już zarejestrowany i skonfigurowany pod kątem Firebase.
Jeśli nie masz projektu Unity, możesz pobrać przykładową aplikację.
Dodaj do projektu Unity pakiet SDK Firebase Unity (a konkretnie
FirebaseDatabase.unitypackage).
Pamiętaj, że dodanie Firebase do projektu Unity wymaga wykonania zadań zarówno w konsoli Firebase, jak i w otwartym projekcie Unity (np. pobierasz pliki konfiguracji Firebase z konsoli, a następnie przenosisz je do projektu Unity).
Zapisywanie danych
Dane do Firebase Realtime Database można zapisywać na 5 sposobów:
| Metoda | Typowe zastosowania |
|---|---|
SetValueAsync() |
Zapisywanie lub zastępowanie danych w zdefiniowanej ścieżce, np. users/<user-id>/<username>. |
SetRawJsonValueAsync() |
Zapisz lub zastąp dane nieprzetworzonymi danymi JSON, np. users/<user-id>/<username>. |
Push() |
dodawać 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() |
Zaktualizuj niektóre klucze dla zdefiniowanej ścieżki bez zastępowania wszystkich danych. |
RunTransaction() |
aktualizować złożone dane, które mogłyby zostać uszkodzone przez jednoczesne aktualizacje; |
Pobieranie DatabaseReference
Aby zapisać 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 odwołaniu
Podstawowe operacje zapisu
W przypadku podstawowych operacji zapisu możesz użyć metody SetValueAsync(), aby zapisać dane w określonej lokalizacji, zastępując wszystkie istniejące dane w tej ścieżce. Za pomocą tej metody możesz przekazywać typy, które odpowiadają 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ć wbudowanego obiektu JsonUtility.ToJson(), aby przekonwertować obiekt na nieprzetworzony 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; } }
Aby dodać użytkownika za pomocą 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); }
Użycie w ten sposób metody SetValueAsync() lub SetRawJsonValueAsync() powoduje zastąpienie danych w określonej lokalizacji, w tym we wszystkich węzłach podrzędnych. Możesz jednak zaktualizować obiekt podrzędny bez konieczności ponownego zapisywania całego obiektu. Jeśli chcesz zezwolić użytkownikom na aktualizowanie profili, możesz zmienić nazwę użytkownika w ten sposób:
mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);
Dołącz do listy danych
Aby dołączyć dane do listy w aplikacjach dla wielu użytkowników, użyj metody Push().
Metoda Push() generuje unikalny klucz za każdym razem, gdy do określonego odwołania Firebase dodawany jest nowy element podrzędny. Dzięki użyciu tych wygenerowanych automatycznie 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ć odwołania do nowych danych zwróconych przez metodę Push(), aby uzyskać wartość automatycznie wygenerowanego klucza elementu podrzędnego lub ustawić dane dla elementu podrzędnego. Wywołanie metody Key na odwołaniu Push() zwraca wartość wygenerowanego automatycznie klucza.
Aktualizowanie określonych pól
Aby jednocześnie zapisywać dane w określonych węzłach 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 podrzędne niższego poziomu, określając ścieżkę klucza. Jeśli dane są przechowywane w kilku lokalizacjach, aby zapewnić lepsze skalowanie, możesz zaktualizować wszystkie instancje tych danych za pomocą rozpowszechniania danych. Na przykład gra może mieć klasę LeaderboardEntry, która wygląda tak:
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ć wpis LeaderboardEntry i jednocześnie zaktualizować go w kanale ostatnich wyników i 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(), aby utworzyć wpis w węźle zawierającym wpisy dla wszystkich użytkowników w /scores/$key i jednocześnie pobrać klucz za pomocą Key. Klucz może być następnie użyty do utworzenia drugiego wpisu w wynikach użytkownika pod adresem /user-scores/$userid/$key.
Korzystając z 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, w którym nowy wpis jest tworzony w obu lokalizacjach. Jednoczesne aktualizacje dokonywane w ten sposób są niepodzielne: albo wszystkie aktualizacje się powiodą, albo wszystkie zakończą się niepowodzeniem.
Usuń dane
Najprostszym sposobem na usunięcie danych jest wywołanie metody RemoveValue() w odniesieniu do lokalizacji tych danych.
Możesz też usunąć, podając null jako wartość innej operacji zapisu, np. SetValueAsync() lub UpdateChildrenAsync(). Możesz użyć tej techniki z UpdateChildrenAsync(), aby usunąć wiele elementów podrzędnych w jednym wywołaniu interfejsu API.
Dowiedz się, kiedy Twoje dane są zatwierdzane.
Aby wiedzieć, kiedy 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 z jakiegoś powodu wywołanie się nie powiedzie, właściwość IsFaulted zadania będzie miała 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. licznikami przyrostowymi, możesz użyć operacji transakcji.
Oceniasz tę operację na Func. Ta aktualizacja Func przyjmuje bieżący stan danych jako argument i zwraca nowy stan, który chcesz zapisać. Jeśli inny klient zapisze dane w lokalizacji, zanim nowa wartość zostanie pomyślnie zapisana, funkcja aktualizacji zostanie wywołana ponownie 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 z 5 najlepszymi wynikami:
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łowemu działaniu tabeli wyników, gdy wielu użytkowników zapisuje wyniki w tym samym czasie lub klient ma nieaktualne dane. Jeśli transakcja zostanie odrzucona, serwer zwraca bieżącą wartość do klienta, który ponownie uruchamia transakcję ze zaktualizowaną wartością. Proces ten jest powtarzany, dopóki transakcja nie zostanie zaakceptowana lub nie zostanie przekroczona liczba prób.
Zapisywanie danych offline
Jeśli klient utraci połączenie sieciowe, aplikacja będzie nadal działać prawidłowo.
Każdy klient połączony z bazą danych Firebase utrzymuje własną wewnętrzną wersję wszystkich aktywnych danych. Gdy dane są zapisywane, najpierw są zapisywane w tej wersji lokalnej. Klient Firebase synchronizuje te dane ze zdalnymi serwerami baz danych i innymi klientami na zasadzie „najlepszych starań”.
W rezultacie wszystkie zapisy w bazie danych natychmiast wywołują zdarzenia lokalne, zanim jakiekolwiek dane zostaną zapisane na serwerze. Oznacza to, że aplikacja pozostaje responsywna niezależnie od opóźnień 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 niestandardowego kodu.