Подготовка
Чтобы использовать Realtime Database, необходимо:
Зарегистрируйте проект Unity и настройте его для использования Firebase.
Если в проекте уже используется Firebase, можете пропустить этот шаг.
Если у вас нет проекта Unity, вы можете скачать пример приложения.
Добавьте в свой проект Unity Unity SDK для Firebase (в частности,
FirebaseDatabase.unitypackage).
Обратите внимание, что для добавления 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
Чтобы записать данные в базу данных, вам нужен экземпляр 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, следующим образом:
stringlongdoubleboolDictionary<string, Object>List<Object>
Если вы используете типизированный объект C#, то можете преобразовать его в необработанный JSON с помощью встроенного метода JsonUtility.ToJson() и вызвать 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<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.
Используя эти пути, вы можете одновременно обновлять несколько местоположений в дереве 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 => { 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 синхронизирует эти данные с удаленными серверами баз данных и другими клиентами по мере возможности.
В результате все записи в базу данных немедленно вызывают локальные события, прежде чем какие-либо данные будут записаны на сервер. Это означает, что ваше приложение остается отзывчивым независимо от задержки сети или подключения.
После восстановления подключения приложение получает соответствующий набор событий, чтобы клиент синхронизировался с текущим состоянием сервера без необходимости писать какой-либо пользовательский код.