Сохранить данные

Прежде чем начать

Прежде чем вы сможете использовать Realtime Database , вам необходимо:

  • Зарегистрируйте свой проект Unity и настройте его для использования Firebase.

    • Если ваш проект Unity уже использует Firebase, значит, он уже зарегистрирован и настроен для Firebase.

    • Если у вас нет проекта Unity, вы можете скачать образец приложения .

  • Добавьте Firebase Unity SDK (в частности, FirebaseDatabase.unitypackage ) в свой проект Unity.

Обратите внимание, что добавление Firebase в ваш проект Unity включает в себя задачи как в консоли Firebase , так и в вашем открытом проекте Unity (например, вы загружаете файлы конфигурации Firebase из консоли, а затем перемещаете их в свой проект Unity).

Сохранение данных

Существует пять методов записи данных в Firebase Realtime Database :

Метод Обычное использование
SetValueAsync() Запишите или замените данные по определенному пути, например, users/<user-id>/<username> .
SetRawJsonValueAsync() Запишите или замените данные необработанным Json, например, users/<user-id>/<username> .
Push() Добавить в список данных. Каждый раз, когда вы вызываете Push() , Firebase генерирует уникальный ключ, который также можно использовать в качестве уникального идентификатора, например user-scores/<user-id>/<unique-score-id> .
UpdateChildrenAsync() Обновите некоторые ключи для определенного пути, не заменяя все данные.
RunTransaction() Обновите сложные данные, которые могут быть повреждены одновременными обновлениями.

Получить ссылку на базу данных

Чтобы записать данные в базу данных, вам понадобится экземпляр 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() . Например, у вас может быть класс 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() чтобы получить значение автоматически сгенерированного ключа дочернего элемента или установить данные для дочернего элемента. Вызов Key по ссылке Push() возвращает значение автоматически сгенерированного ключа.

Обновить определенные поля

Чтобы одновременно записывать определенные дочерние узлы без перезаписи других дочерних узлов, используйте метод 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&ltstring, Object&gt ToDictionary() {
        Dictionary&ltstring, Object&gt result = new Dictionary&ltstring, Object&gt();
        result["uid"] = uid;
        result["score"] = score;

        return result;
    }
}

Чтобы создать запись в таблице лидеров и одновременно обновить ее до последней ленты результатов и собственного списка результатов пользователя, игра использует такой код:

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&ltstring, Object&gt entryValues = entry.ToDictionary();

    Dictionary&ltstring, Object&gt childUpdates = new Dictionary&ltstring, Object&gt();
    childUpdates["/scores/" + key] = entryValues;
    childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

    mDatabase.UpdateChildrenAsync(childUpdates);
}

В этом примере используется Push() для создания записи в узле, содержащей записи для всех пользователей в /scores/$key , и одновременного получения ключа с помощью Key . Затем ключ можно использовать для создания второй записи в оценках пользователя в /user-scores/$userid/$key .

Используя эти пути, вы можете выполнять одновременные обновления в нескольких местах в дереве JSON с помощью одного вызова UpdateChildrenAsync() , например, как в этом примере создается новая запись в обоих местах. Одновременные обновления, выполняемые таким образом, являются атомарными: либо все обновления выполняются успешно, либо все обновления завершаются неудачно.

Удалить данные

Самый простой способ удалить данные — вызвать RemoveValue() по ссылке на расположение этих данных.

Вы также можете удалить, указав null в качестве значения для другой операции записи, например SetValueAsync() или UpdateChildrenAsync() . Вы можете использовать этот метод с UpdateChildrenAsync() для удаления нескольких дочерних элементов за один вызов API.

Знайте, когда ваши данные фиксируются.

Чтобы узнать, когда ваши данные будут переданы на сервер Firebase Realtime Database , вы можете добавить продолжение. И SetValueAsync() , и UpdateChildrenAsync() возвращают Task , который позволяет узнать, когда операция завершена. Если вызов по какой-либо причине не удался, Tasks IsFaulted будет иметь значение true со свойством Exception указывающим, почему произошел сбой.

Сохраняйте данные как транзакции

При работе с данными, которые могут быть повреждены в результате одновременных изменений, например с инкрементными счетчиками, вы можете использовать операцию транзакции . Вы присваиваете этой операции Func . Это обновление Func принимает текущее состояние данных в качестве аргумента и возвращает новое желаемое состояние, которое вы хотите записать. Если другой клиент записывает в это место до того, как ваше новое значение будет успешно записано, ваша функция обновления вызывается снова с новым текущим значением, и запись повторяется.

Например, в игре вы можете разрешить пользователям обновлять таблицу лидеров с пятью наивысшими баллами:

private void AddScoreToLeaders(string email, 
                               long score,
                               DatabaseReference leaderBoardRef) {

    leaderBoardRef.RunTransaction(mutableData =&gt {
      List&ltobject&gt leaders = mutableData.Value as List&ltobject>

      if (leaders == null) {
        leaders = new List&ltobject&gt();
      } else if (mutableData.ChildrenCount &gt= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary&ltstring, object&gt)) continue;
          long childScore = (long)
                      ((Dictionary&ltstring, object&gt)child)["score"];
          if (childScore &lt minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore &gt 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&ltstring, object&gt newScoreMap =
                       new Dictionary&ltstring, object&gt();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

Использование транзакции предотвращает некорректность таблицы лидеров, если несколько пользователей одновременно записывают результаты или у клиента есть устаревшие данные. Если транзакция отклонена, сервер возвращает текущее значение клиенту, который снова запускает транзакцию с обновленным значением. Это повторяется до тех пор, пока транзакция не будет принята или не будет предпринято слишком много попыток.

Запись данных в автономном режиме

Если клиент потеряет сетевое соединение, ваше приложение продолжит работать правильно.

Каждый клиент, подключенный к базе данных Firebase, поддерживает свою собственную внутреннюю версию любых активных данных. Когда данные записываются, они сначала записываются в эту локальную версию. Затем клиент Firebase синхронизирует эти данные с удаленными серверами баз данных и с другими клиентами по принципу «максимально возможно».

В результате все записи в базу данных немедленно вызывают локальные события, прежде чем какие-либо данные будут записаны на сервер. Это означает, что ваше приложение остается отзывчивым независимо от задержки в сети или подключения.

После восстановления подключения ваше приложение получает соответствующий набор событий, чтобы клиент синхронизировался с текущим состоянием сервера без необходимости писать какой-либо собственный код.

Следующие шаги