Zapisz dane

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 w Unity korzysta już z Firebase, jest już zarejestrowany i skonfigurowany pod kątem tej usługi.

    • Jeśli nie masz projektu Unity, możesz pobrać próbną aplikację.

  • Dodaj pakiet SDK Firebase Unity (szczególnie plik FirebaseDatabase.unitypackage) do projektu Unity.

Pamiętaj, że dodanie Firebase do projektu Unity wymaga wykonania zadań zarówno w konsoli Firebase, jak i w otwartym projekcie Unity (np. pobieranie plików konfiguracji Firebase z konsoli i przenoszenie ich do projektu Unity).

Zapisywanie danych

Dane do pliku Firebase Realtime Database można zapisywać na 5 sposobów:

Metoda Typowe zastosowania
SetValueAsync() zapisywać lub zastępować dane w określonej ścieżce, takiej jak users/<user-id>/<username>.
SetRawJsonValueAsync() Zapisz lub zastąp dane nieprzetworzonym plikiem JSON, np. users/<user-id>/<username>.
Push() Dodaj do listy danych. Za każdym razem, gdy wywołujesz funkcję Push(), Firebase generuje unikalny klucz, który może też służyć jako unikalny identyfikator, np. user-scores/<user-id>/<unique-score-id>.
UpdateChildrenAsync() zaktualizować niektóre klucze na zdefiniowanej ścieżce bez zastępowania wszystkich danych;
RunTransaction() Aktualizowanie złożonych danych, które mogą zostać uszkodzone przez równoczesne aktualizacje.

Pobieranie obiektu 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;
  }
}

zapisywać, aktualizować i usuwać dane w referencji;

Podstawowe operacje zapisu

W przypadku podstawowych operacji zapisu możesz użyć funkcji SetValueAsync(), aby zapisać dane w określonym odwołaniu, zastępując wszystkie istniejące dane na tej ścieżce. Za pomocą tej metody możesz przekazywać typy, które odpowiadają dostępnym typom JSON w ten sposób:

  • string
  • long
  • double
  • bool
  • Dictionary<string, Object>
  • List<Object>

Jeśli używasz typowanego obiektu C#, możesz użyć wbudowanej funkcji JsonUtility.ToJson() do konwertowania obiektu na surowy JSON i wywołania funkcji SetRawJsonValueAsync(). Na przykład klasa User może 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 z uprawnieniami SetRawJsonValueAsync(), wykonaj te czynności:

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 wartości SetValueAsync() lub SetRawJsonValueAsync() spowoduje zastąpienie danych w wybranej lokalizacji, w tym wszystkich węzłów podrzędnych. Nadal możesz jednak aktualizować obiekt podrzędny bez konieczności przepisywania 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łączać dane do listy w aplikacji wielostanowiskowej, użyj metody Push(). Metoda Push() generuje unikalny klucz za każdym razem, gdy do określonego odwołania Firebase dodawane jest nowe dziecko. Dzięki tym automatycznie generowanym kluczom dla każdego nowego elementu na liście kilka klientów może dodawać podelementy do tej samej lokalizacji w tym samym czasie bez konfliktów podczas zapisu. Unikalny klucz wygenerowany przez funkcję Push() jest oparty na sygnaturze czasowej, więc elementy listy są automatycznie sortowane chronologicznie.

Możesz użyć odwołania do nowych danych zwróconych przez metodę Push(), aby uzyskać wartość automatycznie wygenerowanego klucza podrzędnego lub ustawić dane dla tego podrzędnego. Wywołanie funkcji Key w przypadku odwołania Push() zwraca wartość klucza wygenerowanego automatycznie.

Aktualizowanie konkretnych pól

Aby jednocześnie zapisywać dane w określonych węzłach podrzędnych bez nadpisywania innych węzłów podrzędnych, użyj metody UpdateChildrenAsync().

Podczas wywoływania funkcji UpdateChildrenAsync() możesz aktualizować wartości podrzędne niższego poziomu, podając ścieżkę klucza. Jeśli dane są przechowywane w kilku lokalizacjach, aby zapewnić lepszą skalowalność, możesz zaktualizować wszystkie wystąpienia tych danych za pomocą rozgałęzienia 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ć pozycję na tablicy wyników i jednocześnie zaktualizować ją w pliku danych o ostatnich wynikach oraz na liście wyników użytkownika, gra używa takiego kodu:

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 funkcja Push() służy do utworzenia wpisu w węźle zawierającym wpisy wszystkich użytkowników w węźle /scores/$key oraz do jednoczesnego pobrania klucza za pomocą funkcji Key. Klucza można użyć do utworzenia drugiego wpisu w tablicy wyników użytkownika na stronie /user-scores/$userid/$key.

Dzięki tym ścieżkom możesz przeprowadzać jednoczesne aktualizacje wielu lokalizacji w drzewie JSON za pomocą jednego wywołania funkcji UpdateChildrenAsync(). W tym przykładzie nowy wpis jest tworzony w obu lokalizacjach. Jednoczesne aktualizacje w ten sposób są atomowe: albo wszystkie się powiodą, albo wszystkie się nie powiodą.

Usuń dane

Najprostszym sposobem usuwania danych jest wywołanie funkcji RemoveValue() z odniesieniem do lokalizacji tych danych.

Możesz też usunąć wartość, podając null jako wartość dla innej operacji zapisu, takiej jak SetValueAsync() lub UpdateChildrenAsync(). Możesz użyć tej techniki za pomocą funkcji UpdateChildrenAsync(), aby usunąć wiele elementów podrzędnych w jednym wywołaniu interfejsu API.

Dowiedz się, kiedy Twoje dane są zapisywane.

Aby dowiedzieć się, kiedy Twoje dane zostaną zapisane na serwerze Firebase Realtime Database, możesz dodać kontynuację. Zarówno SetValueAsync(), jak i UpdateChildrenAsync() zwracają wartość Task, która informuje o zakończeniu operacji. Jeśli wywołanie zakończy się niepowodzeniem z jakiegokolwiek powodu, w elementach zadań IsFaulted będzie wartość true, a w elementach Exception – właściwa wartość wskazująca przyczynę niepowodzenia.

Zapisywanie danych jako transakcji

Podczas pracy z danymi, które mogą zostać uszkodzone przez równoległe modyfikacje, np. z licznikami przyrostowymi, możesz użyć operacji transakcji. Operacja ta ma nazwę Func. Ta aktualizacja Func przyjmuje jako argument bieżący stan danych i zwraca nowy stan, który chcesz zapisać. Jeśli inny klient zapisze w tej lokalizacji nową wartość, zanim uda się zapisać Twoją nową wartość, funkcja aktualizacji zostanie wywołana ponownie z nową bieżącą wartością i ponownie spróbuje zapisać dane.

W grze możesz na przykład zezwolić użytkownikom na aktualizowanie tabeli wyników za pomocą 5 najlepszych 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);
    });
}

Transakcja zapobiega błędom w tablicy liderów, jeśli wyniki rejestruje jednocześnie wielu użytkowników lub klient ma nieaktualne dane. Jeśli transakcja zostanie odrzucona, serwer zwróci bieżącą wartość klientowi, który ponownie wykona transakcję z aktualną wartością. Czynność ta jest powtarzana, dopóki transakcja nie zostanie zaakceptowana lub nie zostanie podjęta zbyt duża liczba prób.

Zapisywanie danych 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ą wersję wewnętrzną wszystkich aktywnych danych. Dane są najpierw zapisywane w tej wersji lokalnej. Następnie klient Firebase synchronizuje te dane ze zdalnymi serwerami bazy danych i z innymi klientami według zasady „najlepszego wysiłku”.

W rezultacie wszystkie zapisy w bazie danych powodują natychmiastowe wywołanie zdarzeń lokalnych, zanim jakiekolwiek dane zostaną zapisane na serwerze. Oznacza to, że aplikacja pozostaje responsywna niezależnie od opóźnień w 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.

Następne kroki