I am trying to make a system in my game where a dictionary stores the world name (key) then the level the player is on, alongside the stars the player has collected on that exact level (values) and possibly other values like how many times said player failed the level, etc.
Any help on how to do this would be highly appreciated
Note: I’m still learning all about dictionarys as I think they are probably the most useful and time efficient programming technique I’ve come across in c#. If there are better methods, I’d love to know too.
You would make an object that encapsulates all this data, namely a regular c# class.
However, Unity cannot serialise dictionaries for editing via the inspector. That said, if this is only save data then you probably don’t need to edit in the inspector and third party serialisers like Newtonsoft.JSON and the Odin serialiser can write dictionaries to disk no problem.
If you need to edit these values in the inspector, just use a regular List<T>.
I want to store data in this dictionary and i dont really need it to be shown in inspector so ima use a c# class. However i am having trouble figuring out how to implement it
This is the code i have so far
[System.Serializable]
public class __LvlDataStore
{
public int BiomeLvl;
public int StarsCollecedOnLvl;
}
[System.Serializable]
public class GameData
{
public SerializableDictionary<string, __LvlDataStore> Level;
public GameData()
{
Level = new SerializableDictionary<string, __LvlDataStore>()
{
["Tutorial"] = [0,0], // i dont know how to get my values in here!!
["City"] = 1,
};
}
}
That’s not in the constructor per se, it’s a collection initializer that’s implicitly using the indexer to set values, and this runs after the constructor. It’s used as a technique to make initialization look cleaner / more like initialization (to some). There’s also the syntax new() { { key, value }, ... } which calls .Add.
Just initialize each instance of __LvlDataStore as you see fit. This is basic C#: you call a constructor to set up the object, and optionally set more values after getting the constructed object.
// parameterless constructor, you either write one or let the compiler generate one for you that basically provides a blank object
__LvlDataStore value = new __LvlDataStore();
value.BiomeLvl = 100;
value.StarsCollecedOnLvl = 200;
// or, if you create a constructor taking biome level and stars collected:
__LvlDataStore value = new __LvlDataStore(100, 200);
You can also use the object initializer feature of C# to set values immediately after calling the constructor:
The fact that similar data is saved on a per-level basis is simply a detail.
Here’s some more relevant reading:
Load/Save steps:
An excellent discussion of loading/saving in Unity3D by Xarbrough:
And another excellent set of notes and considerations by karliss_coldwild:
Loading/Saving ScriptableObjects by a proxy identifier such as name:
When loading, you can never re-create a MonoBehaviour or ScriptableObject instance directly from JSON. Save data needs to be all entirely plain C# data with no Unity objects in it.
The reason is they are hybrid C# and native engine objects, and when the JSON package calls new to make one, it cannot make the native engine portion of the object, so you end up with a defective “dead” Unity object.
Instead you must first create the MonoBehaviour using AddComponent() on a GameObject instance, or use ScriptableObject.CreateInstance() to make your SO, then use the appropriate JSON “populate object” call to fill in its public fields.
If you want to use PlayerPrefs to save your game, it’s always better to use a JSON-based wrapper such as this one I forked from a fellow named Brett M Johnson on github:
Do not use the binary formatter/serializer: it is insecure, it cannot be made secure, and it makes debugging very difficult, plus it actually will NOT prevent people from modifying your save data on their computers.