Подготовка
Чтобы использовать Realtime Database, вам необходимо:
Зарегистрируйте проект Unity и настройте его для использования Firebase.
Если в проекте уже используется Firebase, можете пропустить этот шаг.
Если у вас нет проекта Unity, вы можете скачать пример приложения.
Добавьте в свой проект Unity Firebase Unity SDK (в частности,
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#, вы можете использовать встроенный метод 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<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 синхронизирует эти данные с удаленными серверами базы данных и другими клиентами по мере возможности.
В результате все записи в базу данных немедленно активируют локальные события, прежде чем какие-либо данные будут записаны на сервер. Это значит, что приложение будет работать без сбоев независимо от задержки сети или качества подключения.
После восстановления подключения приложение получит нужный набор событий, чтобы клиент синхронизировался с текущим состоянием сервера без необходимости писать какой-либо код.