Home > OS >  Complex data structures with ScriptableObjects in Unity3D
Complex data structures with ScriptableObjects in Unity3D

Time:12-25

I am trying to create a data structure like in the picture, where I can easily add and remove the reward type. It is for a roguelike level-up system where basically I want to get a reward for a specific character like a warrior or mage from a specific rarity.

I am more experienced with Lua than Unity and in Lua language, it would look like this,

`{
    ["Melee"] = 
    
    {
        [1] = {
            [1] = {"Stat", "Strength", 10},
            [2] = {"Ability", "Swirl", 1},
            
        },
        
        [2] = {
            [1] = {"Stat", "Strength", 50},
            [2] = {"Stat", "PhysicalDefense", 10},
        },
    },
    
    ["Healer"] = 
        {
        [1] = {
            [1] = {"Stat", "MagicalAttack", 5},
            [2] = {"Ability", "Regeneration", 1},
        },

        [2] = {

            [1] = {"Stat", "MagicalDefense", 15},
            [2] = {"Ability", "Regeneration", 1},
        },
    },
}` 

then for getting a spesific reward I would do reward = ["Melee][1][1]. However, in Unity, using dictionaries disables scriptableobject's function to add elements inside editor. So how can I create a scriptableobject that I can add elements inside the editor with the same structure?

CodePudding user response:

You don't need dictionaries in order to access items by index.

Simply use a plain array or List and do

public class Warrior : ScriptableObject
{
    public Rarity[] rarities;
}

public class Rarity : ScriptableObject 
{
    public Reward[] rewards;
}

public class Reward : ScriptableObject
{
    public enum RewardType { Stat, Ability }

    public RewardType type;
    public int value;
}

And finally reference these in a

public Warrior[] warriors;

Then if really needed for the first level you can still setup a dictionary on runtime like e.g.

public Dictionary<string, Warrior> Warriors = new ();

private void Start ()
{
    foreach(var warrior in warriors)
    {
        Warriors.Add(warrior.name, warrior);
    }
}

Then the rest you would access like e.g.

var reward = Warriors["Melee"].rarities [0].rewards[1];
Debug.Log($"{reward.type} - {reward.name} - {reward.value}");

Have in mind that in c# indices are 0-based


However, if you are more familiar with your way and prefer a general dictionary approach you can totally do so!

You could simply write this as a JSON and can then chose among the various JSON libraries to convert it into a dictionary again.

{
    "Melee" : [
        [ 
            {
                "type":"Stat", 
                "name":"Strength", 
                "value":10
            },
            {
                "type":"Ability", 
                "name":"Swirl",
                "value":1
            }            
        ],
        ...
    ],
    "Healer" : ...
}

then you would not use ScriptableObject and only use e.g.

[Serializable]
public class Reward
{
    public enum RewardType { Stat, Ability }

    public RewardType type;
    public int value;
    public string name;
}

and then deserialize this into e.g.

var Warriors = JsonConvert.DeserializeObject<Dictionary<string, Reward[][]>>(theJsonString);
var reward = Warriors["Melee"][0][0];
Debug.Log($"{reward.type} - {reward.name} - {reward.value}");

where theJsonString can be read form a file or a web request etc

This example uses Newtonsoft JSON.Net which is available for Unity via the package manager.

Personally I would though rather go with explicitly named fields and classes instead of those jagged arrays.

  • Related