Database on ScriptableObject with save / load system

Introduction



Every game has data that game designers work with. In RPG, this is a database of items, in match-3 - the cost in crystals of tools from the store, in action games - the amount of HP that the first-aid kit heals.



There are many ways to store such data - someone stores it in tables, in xml or json files, which they edit with their own tools. Unity provides its own way - Scriptable Objects (SO), which I like because you don't need to write your own editor for their visual presentation, it's easy to link to game assets and to each other, and with the advent of Addressables, this data can be easily and conveniently stored outside games and update separately.



In this article, I would like to talk about my SODatabase library, with which you can conveniently create, edit and use in the game (edit and serialize) scriptable objects.



Creating and editing SO



I create and edit SOs in a separate window, which is somewhat similar to the project windows with an inspector - on the left there is a folder tree (the folder in which all SOs are located - a group in addressables), and on the right is the inspector of the selected SO.



Interface



To draw such a WindowEditor, I use the Odin Inspector library . Also, I'm using the serialization for SO from this library - it greatly extends the standard unitium serialization, allowing polymorphic classes, deep nesting, and class references to be stored.



Creating SO



New SOs are created by clicking a button in this window - there you need to select the type of the desired bipod, and it is created in the folder. In order for the SO type to appear in this window as an option, SO must inherit from DataNode, which has only one additional field to ScriptableObject



public string FullPath { get; }


SO, .



SO



- , , SO - , — , , SO.

static SODatabase , , .



public static T GetModel<T>(string path) where T : DataNode   

public static List<T> GetModels<T>(string path, bool includeSubFolders = false) where T : DataNode


, SODatabase , Addressables.





ScriptableObject , . ScriptableObject . , SO .



— , , - , . , SO . , unity, xml .



, ScriptableObject JSON.



DataNode — SO, SODatabase,



[JsonObject(MemberSerialization.OptIn, IsReference = true)]


JsonProperty save.txt . SODatabase addressables JsonConvert.PopulateObject SODatabase, .



, , SO ( , JsonProperty) -, SO . — . , , , .





-



async void Awake()
{
    await SODatabase.InitAsync(null, null);
    await SODatabase.LoadAsync();
}




private void OnApplicationPause(bool pauseStatus)
{
    if (pauseStatus)
        SODatabase.Save();
}

private void OnApplicationQuit()
{
    SODatabase.Save();
}        


PlayerSO, — , , . - , SODatabase, .



public class PlayerSO : DataNode
{
    public static string Path => "PlayerInfo/Player";

    [JsonProperty]
    public string Title = string.Empty;

    [JsonProperty]
    public int Experience;
}


PlayerInventorySO, ( SO SODatabase).



 public class PlayerInventorySO : DataNode
 {
     public static string Path => "PlayerInfo/PlayerInventory";

     [JsonProperty]
     public List<ItemSO> Items = new List<ItemSO>();
 }


, — , . , , QuestSO (, , ..) . - .



public class QuestNode : BaseNode
{
    public static string Path = "QuestNodes";

    //Editor
    public virtual string Title { get; } = string.Empty;

    public virtual string Description { get; } = string.Empty;

    public int TargetCount;

    //Runtime
    [JsonProperty]
    private bool finished;
    public bool Finished
    {
        get => finished;
        set => finished = value;
    }
}


, JsonProperty , SO .



var playerSO = SODatabase.GetModel<PlayerSO>(PlayerSO.Path);
var playerInventorySO = SODatabase.GetModel<PlayerInventorySO>(PlayerInventorySO.Path);
var questNodes = SODatabase.GetModels<QuestNode>(QuestNode.Path, true);




— - / , , SO, json. - , ( ..) . SO , SODatabase , Addressables.



addressables ( ). .



The library is publicly available on github . Written using Nullable from c # 8, therefore requires Unity 2020.1.4 as a minimum version.




All Articles