Найти в Дзене
Hyaena Games

Unity - удаленное обновление данных #3 (Firebase)

Оглавление

Целевая аудитория: начинающие разработчики

Теги: Unity, ScriptableObject, Json, JsonUtility, Firebase

Предыдущие статьи обязательные для прочтения.

Вступление

В продолжение предыдущей статьи, об удаленном обновлении данных, настало время разобраться с сервисом Firebase, благодаря которому мы будем хранить и получать наши конфиги для ScriptableObject.

Firebase

Ссылка на официальную интеграцию с Unity

Перейдя по ссылке, вы получите инструкцию как создать проект в Firebase, все настроить и интегрировать SDK в Unity. Она довольна простая, из минусов, она на английском языке, но думаю большей проблемой это не будет. Вкратце опишу список действий, которые необходимо выполнить:

  • Создать проект в Firebase
  • Зарегистрировать приложение Android или iOS приложение, указав bundle id, название приложения
  • Скачать файл конфигурации который выдаст вам Firebase после первого этапа регистрации (для android этот файл называется: google-services.json). Остальные шаги при регистрации можно пропустить
  • Скачать Firebase Unity SDK
  • Импортировать FirebaseAuth.unitypackage, FirebaseRemoteConfig.unitypackage
  • Добавить в папку Assets проекта ранее скаченный google-services.json
  • На этом конфигурацию можно считать завершенной

Добавления JSON в Firebase

Давайте в продолжение предыдущей статьи добавим JSON для WeaponData в Firebase. Для этого нам необходимо:

  • Перейти в раздел Grow/Remote config
  • Добавить новый параметр. Где ключ название нашего ScriptableObject в проекте, в данном случае это WeaponDataScriptableObject и значение по умолчанию:
{
"Id":"AK-47",
"Damage":100,
"FireRate":0.1,
"ReloadDuration":2.9,
"AmmoCount":30
}

Результат наших действий, JSON успешно добавлено
Результат наших действий, JSON успешно добавлено
Всплывающие окно при нажатии на параметр, выдела кнопка редактирования JSON
Всплывающие окно при нажатии на параметр, выдела кнопка редактирования JSON
Окно редактирования и валидации JSON
Окно редактирования и валидации JSON
  • Нажать на кнопку "Опубликовать изменения" !!!

На это все необходимые действия в Firebase закончены.

Клиентская часть для Unity

Теперь необходимо написать клиентскую часть, которая будет подключаться к Firebase, получать нужные JSON и десериализовывать их в ScriptableObject. Но для этого нам необходимо внести некоторые изменения.

Добавим интерфейс IDeserializeFromJson, который информирует нас о том, что данный объект (в нашем случае ScriptableObject) умеет десериализовывать себя из JSON:

public interface IDeserializeFromJson
{
void Deserialize(string json);
}

И изменим наш WeaponDataScriptableObject класс из прошлой статьи следующим образом:

using Scipts;
using UnityEngine;

[System.Serializable]
public class WeaponData
{
public string Id;
public int Damage;
public float FireRate;
public float ReloadDuration;
public int AmmoCount;
}

[CreateAssetMenu(fileName = "WeaponData", menuName= "ScriptableObjects/WeaponData")]
[System.Serializable]
public class WeaponDataScriptableObject : ScriptableObject,
IDeserializeFromJson
{
public WeaponData WeaponData;

public void Deserialize(string json)
{
WeaponData = JsonUtility.FromJson<WeaponData>(json);
}

}

Жирным выделено то, что было добавлено. А именно:

  • WeaponDataScriptableObject теперь не только наследуется от ScriptableObject, но и реализует интерфейс IDeserializeFromJson
  • Добавлен метод Deserialize который реализует интерфейс IDeserializeFromJson

JsonUtility.FromJson - должно быть знакомо вам из прошлой статьи.

Теперь напишем класс, который будет иметь ссылки на все ScriptableObject которые необходимо удаленно обновлять, подключаться к Firebase, получать JSON'ы и десериализировать их в необходимые ScriptableObject:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Firebase;
using Firebase.Extensions;
using Firebase.RemoteConfig;
using UnityEngine;

namespace Scipts
{
public class RemoteConfigs : MonoBehaviour
{
[SerializeField] private ScriptableObject[] _scriptableObjects;

private void Start()
{
StartFetchConfigs();
}

public void StartFetchConfigs()
{
var defaultConfigs = _scriptableObjects.ToDictionary(k => k.name, v => (object)string.Empty);

FirebaseRemoteConfig.SetDefaults(defaultConfigs);
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
{
if (task.Result == DependencyStatus.Available)
{
FirebaseRemoteConfig.FetchAsync(TimeSpan.Zero).ContinueWithOnMainThread(FetchComplete);
}
});
}

private void FetchComplete(Task fetchTask)
{
if (fetchTask != null && fetchTask.IsCompleted)
{
var info = FirebaseRemoteConfig.Info;
if (info != null && info.LastFetchStatus == LastFetchStatus.Success)
{
FirebaseRemoteConfig.ActivateFetched();

foreach (var scriptableObject in _scriptableObjects)
{
var json = FirebaseRemoteConfig.GetValue(scriptableObject.name).StringValue;
if (!string.IsNullOrEmpty(json))
{
var deserializer = scriptableObject as IDeserializeFromJson;
if (deserializer != null)
{
deserializer.Deserialize(json);
}
}
}
}
}
}
}
}

Обсудим важные моменты:

[SerializeField] private ScriptableObject[] _scriptableObjects;

храним ссылки на все _scriptableObjects которые хотим удаленно обновлять. Они должны реализовывать интерфейс IDeserializeFromJson. В нашем случае один из таких классов WeaponDataScriptableObject (предоставлен выше).

var defaultConfigs = _scriptableObjects.ToDictionary(k => k.name, v => (object)string.Empty);

FirebaseRemoteConfig.SetDefaults(defaultConfigs);
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
{
if (task.Result == DependencyStatus.Available)
{
FirebaseRemoteConfig.FetchAsync(TimeSpan.Zero).ContinueWithOnMainThread(FetchComplete);
}
});

Делаем запрос к Firebase, с пустыми дефолтными конфигам, т.к в данном случае, для нас это неважно, мы всегда хотим получать данные с сервера. В качестве калбека передаем метод FetchComplete - он будет вызван, когда данные будут получены.

foreach (var scriptableObject in _scriptableObjects)
{
var json = FirebaseRemoteConfig.GetValue(scriptableObject.name).StringValue;
if (!string.IsNullOrEmpty(json))
{
var deserializer = scriptableObject as IDeserializeFromJson;
if (deserializer != null)
{
deserializer.Deserialize(json);
}
}

Если все хорошо, мы переберем все указанные ScriptableObject в цикле, по их имени постараемся получить JSON. Важно: имена ScriptableObject в проекте и Firebase должны совпадать!

Если JSON получен, и ScriptableObject действительно реализует интерфейс IDeserializeFromJson, мы вызовем метод для десериализации, данные будут установлены - готово!

Настроенный Remote Configs
Настроенный Remote Configs

В случае же, если нет интернета или данные по какой-либо причине небыли получены, они останутся теме же, какие вы их сами настроили в Unity.

Чек-лист для удаленного обновления данных

Таким образом, что бы удаленно обновлять нужный ScriptableObject необходимо произвести следующие действия:

  • Данные в ScriptableObject должны быть выделены в отдельный класс ( пример WeaponData)
  • ScriptableObject должен иметь ссылку на данные (пример WeaponDataScriptableObject):
[CreateAssetMenu(fileName = "WeaponData", menuName = "ScriptableObjects/WeaponData")]
public class WeaponDataScriptableObject : ScriptableObject, IDeserializeFromJson
{
public WeaponData WeaponData;
...
}
  • ScriptableObject должен реализовывать интерфейс IDeserializeFromJson, и соответственно иметь метод Deserialize, который десериализирует данные из JSON, пример:
[CreateAssetMenu(fileName = "WeaponData", menuName= "ScriptableObjects/WeaponData")]
public class WeaponDataScriptableObject : ScriptableObject,
IDeserializeFromJson
{
public WeaponData WeaponData;

public void Deserialize(string json)
{
WeaponData = JsonUtility.FromJson<WeaponData>(json);
}

}
  • Данный ScriptableObject должен быть создан и указан через инспектор в RemoteConfigs
-6
  • В Grow/Remote Config должен быть добавлен параметр, с таким же именем как у файла ScriptableObject в проекте:
-7

Итоги

Мы получили готовое решение, которое позволяет нам удаленно обновлять любые ScriptableObject при этом серьезных изменений делать для этого не нужно, только те, что описаны в чек-листе выше! Система вышла очень простой и гибкой, и ее можно использовать в любых проектах. Надеюсь, вам это пригодится а сам материал был полезен и интересен. Спасибо за внимание, подписывайтесь на канал, и ждите новых интересных статей!