Hi. I wanted to thank you for this tutorial it really helped me out. I was retying your solution to test it out and noticed you are rethrowing the exception using "throw e" at 13:10. For anyone reading this comment please use just the "throw" keyword. If you use "throw e" you will remove part of the call stack. Sorry for pointing stuff like that out. This video is one of the best I've seen showing how to do things properly and I firugred my comment might save someone some pain in the future.
Absolutely fantastic video! RU-vid is full of tutorials for basic implementations, and you presenting the more complex and advanced topics, and doing a deep dive into them, is super useful and refreshing (and also important - this seems to be must-have knowledge to get a job in the industry as a developer). The presentation was fantastic, good explanations, and the pace is great.
Excellent topic. I've been playing with saving and loading in my project and it's a mess. I read not to use binary formatter but there weren't any replacements suggested. it's nice to see how it can be done safe and clean.
Thank you !! Working perfectly with my game, so much easier than my playerPref method , no conversion needed can remove so much code from older method :)
Good tutorial, thanks! However, I would make a backup of the previous save file and delete it only at the end when the new save file has been successfully created. That way, you can be sure that you won't lose the entire save file if the game or the PC crashes / an error occurs during the save.
What do you mean by that? In order to save a new file you should delete the previous isn't it right? Even if you create the new one how would you not have any conflict with the previous one?
@@ibeatboxer_9673 If you delete the previous file, then save the new one you are risking to lose the player's progress. If the game crashes during the file writing or the file writing fails, the previous file is still deleted, but you don't have a new file. So no save file at all anymore. I suggested to make a copy of the previous file, then write the new file and only if this was successful, delete the copied file. This surely is a very unlikely scenario but only one player losing progress is one player too much.
@@ibeatboxer_9673 Maybe change the file extension to .bck before creating a new .json, then delete .bck if the save was successful otherwise revert .bck to .json.
@@ibeatboxer_9673 I mean they have extra work under it where they would just: if exist > load it > create a new file with .bak > then do the work on real one > if something wrong happened with the original > load the .bak instead.
so how do you save complex data? I have a class(inventory item) with image, transform, an enum, a scriptable object and another gameobject as variable. i want to save it and unity is having a hard time converting that into data. do I have to manually break down the data into primitive variables and save them?
If you need to retain object references using JsonUtility is a little easier (you can still do it with Json.NET, but it’s a little extra work). For this type of thing I would have an item scriptable object that had references to the image and prefab and whatever other project files it needs. Whenever I need to instantiate the UI I would read the prefab from the SO and instantiate the prefab
Excellent lecture! Your coding style is clean and professional and teaches good programming habits. I have a hard time tackling Game Serialization and this video gave me some good ideas. One question if I may. I'm working on a Baldur's Gate-like game, with lot's of area transitions and after many days of brainstorming I still can't decide what approch to take for bringing the player party to another area with inventory and equipped armor/weapons. I'm going back and forth between DontDestroyOnLoad and using a persistent scene with additive scenes for each area, but both ways feel wrong and hacky. I seriously would prefer to save everything in a scene, destroy it and reload everything in the next. May I ask you and others, what approach you think would be best or which approach would be horrible to take?
There's a lot of things to consider there. If you're okay having the kind of clunky "fade to black" or "loading screen" the full load of a new scene and just re-instantiating your player/group objects (including weapons/armor, etc..) at the "start point" on the next level is the simplest and most memory efficient way of handling that. For more of an open world feel, I think additively loading the scenes and keeping the players/group always active is the way to go.
@@LlamAcademy Thank you for sharing your view! I think method 1 it is for now. With this serialization system it shouldn't be too hard to implement. I'd really love to see more videos about this topic.
Would it be safer to write a second save file if you find a save file already exists? Then when the file is written successfully, you delete the old save? That way if anything goes wrong during the save it can gracefully revert to the old save?
Maybe? At some point you're going to want to write over the original save. I guess if the save to the temporary "new" file succeeds and the write to the "real" save file fails, you can use the temporary one. If the problem with writing persists (maybe disk space is full or something) then the next time you try to write to the temporary one, that may also fail. You could build a very robust system to manage this kind of thing, but in my opinion, the risk of this is very small. Having a cloud save or cloud backup would be the safest option to prevent total save file loss.
I've run into cases where my data structure goes > 10 layers deep, so Serialization fails. In those cases, I've found that I can store some data classes, like "Inventory" or what not, as a JSON string, and then just save the various strings. On Load, I can load all the data in two passes -- loading the saved data, then loading the saved JSON strings. I'll have to come back after the day job to learn why using JSON Utility is not good! :D
Sounds like you already found one of the issues with JSONUtility 😉. I got some feedback as well instead of using Newtonsoft (JSON.NET) you could use System.Text.Json (docs.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer?view=net-6.0) which is C# native JSON support that works effectively the same, but can even be faster than JSON.NET
Yes, but I think Microsoft does a better job at it than I can, you can see their guidance here: docs.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide
i really enjoyed your video. The only thing i have a gripe with is i still want evidence on why not using the unity JSONs for saving. You said it doesn't support complex types, but your example wasn't complex either and was just nested standard data types in the json object. If you could show in a video one day complex values being stored then maybe I would be swayed in the direction your talking about but your approach seems like there's overheard development without showing a true scenario of it working with the implementation. I still really love the video and watched it till the end
Well, even simple Dictionaries just don’t save with JsonUtility. I’m sure you could come up with an event/observable+visitor system that could collect all objects that have data that need to be saved and write only once to the disk without worrying about multiple things trying to write at once. That would be pretty sophisticated and cool
Great video thank you! I was wondering if using a struct instead of a class for PlayerStats would be better performance-wise (thinking about an auto save system on mobile)?
Generally structs put less pressure on the heap so if you’re saving a lot during gameplay that could be better. I have always used a class for whatever reason and haven’t had issues with the performance, but I also haven’t saved extensively during gameplay where performance is critical.
Saving to registry? As I understand it, MS are moving away from the registry style of storing data. UWP and W8 introduced ApplicationData (and the AppData folder). If apps store data there instead of the registry it makes for an easier cleanup, and prevents performance decay on long-life-installs.
This saves data locally to our device. What would you recommend for managing data across devices using github? Me and my friend are both working on the same unity project, synched up via a git repository. Would it be best to simply sync the folder in appdata too?
Do you need to synchronize the save files between members of your project? That's a little trickier... These are saved (on Windows) in the User directory. You could potentially create a separate git repo in that folder and push changes as needed. Maybe symlinking the persistent data path folder could get you there, but honestly, I haven't tried something like this before. The lowest-tech solution would be to create a folder in your main repo with like "base save files" and manually copy them around as needed
hey, beginner game dev here I was making an arcade game (similar to flappy bird) and I wanted to be able to save the player high score so I could show it at the end of the run (or just at the top corner or something). Anyway I was wondering if you would still recommend this method and if so how would you alter it (as I think some of it might be a bit unnecessary just to save the player's high score). Thanks for any help :)
You can use this method to save high scores. Honestly it may initially seem like a lot, but with the framework in place it’s quite easy to handle even multiple files. If you want to save more than just the highest score, for example the top 10 high scores, this will be very helpful. If you only ever want to store the single highest score, you can achieve that with simply using PlayerPrefs.SetInt(“HighScore”, score); PlayerPrefs.Save(); And retrieve it with PlayerPrefs.GetInt(“HighScore”); That will work and will get you up and running pretty quick. If you plan to extend your game, you might want to go ahead and do this because otherwise you’ll have to undo all the other saving stuff and reimplement something more robust like this eventually anyway. Ultimately it’s up to you and the needs of your game 🙂
How do you link data to a monbehavior since the reference is lost when you open unity again. Such as a graph node and the monbehavior that holds the node data
Hey Llam great video. It would be amazing if you can show how we can save Json to Google play services cloud there's no tutorials on that. It would be amazing keep storing Json somewhere from server side which is totally free. Could you make a video on that would be really helpful. I tried in many ways to save Json to Google cloud but unsuccessful. :)
Hmm 🤔 I'll think about that. I didn't use Google Play services. For my game I used PlayFab which handles that. It's free up to a point, but eventually you have to pay. They don't recommend launching your game on the free tier because it doesn't have an SLA
@@LlamAcademy i used a playfab but there's more limitations that you can expect for some reason playfab didn't mention that player even if you doesn't reach 100K it's calculating each player API call for every single 5 minutes that means that you cannot log-in after that time because of limitations and that gonna cause a lot issues. That's why I moved to google play services cloud which Is absolutely free no metter the amount of players the only things that you cannot do is see the data on the cloud which is stored into google drive account for each player. But at least you can store json or variables into cloud for absolutely free. Of course there is a limitations like you can only store 3MB of data file but I think that should be enough even for big projects since my json file is 3kb at least. But there is actually no documentation about that I see some old forums that they managed to save json to that cloud but I tried and no success maybe because I'm bad at coding :( I see many games storing data to this cloud but I have no idea how. I just managed how to save separately each variables but that's gonna be a mess since a have a lot of data
DateTime.Now.Ticks could be replaced with the Stopwatch class, but DateTime gets the job done as well. This is the best video on this subject that I have seen, thank you!
Hi thank you for the great video! I have a slightly related(?) question regarding game data. When it comes down to having pre-existing data(such as a spreadsheet of enemy/item data), how should I go about packing the data for a game? I have heard people recommending against /Resources and I would like to hear how you would go about handling this data.
Personally I would load the data from a spreadsheet into scriptable objects. I’m not a fan of using csv or excel to define these relationships. Editor fooling around scriptable objects makes it much easier to deal with. However if you and/or your team prefer to work like that, I don’t see a problem with the resources folder.
I making a simple cannon shooting game for mobile, i can't decide whether i should save data on local or cloud for my game, i want to make it run on cloud to make sure internet stays on when playing game. what should i do?
I actually think if you can have your mobile game not require internet connection, you shouldn't. There are many use cases such as on a plane, train, and boat, where the device may not have internet and it would be cool to still be able to play it. I actually spent a good couple months ripping out the "always online" requirement for my game so it could be played offline in these scenarios. If you need it to be online for multiplayer and anti-cheat, sure. If it's "just because" I would advise against it.
Is it possible to save GameObject properties, as an transform.position, rotation, scale etc. And for example a navmesh settings for enemy: target, path, something else. If is not possible which method i should use to do that in my project? Thanks for your help
Yes, but you will need to copy those into a data structure that can be serialized first (a simple class with those properties is fine) and apply them to your target objects on load
I don't think you should be deleting the only other save before creating a new one, that's a fast way to get a corrupted save that is unrecoverable, I would change it to have multiple saves before starting to overwrite the oldest or some similar system.
Hello man, can you help me with this sytem because i can't understand how is it possible. FOr example, i have simple public class with fields and a few Lists with already added variables, for example in my projects it's screenResolutions list. I have list with new(1920, 1080), new(1024, 768), new(1152, 864), ...... 14 elements. For the first game starting, i'm trying to load data from my folder and it work for the first time, but if i want to restart my game(or start it again, 2x, 3x, and more). For all next game starting times my system load this data incorect. How i already said i have list with 14 elements(example) and for the first time system load this data corect, with all this 14 elements but for the next times amount of elements increasing on (amount of elements) + (default count of elements). For example, for the 2 game run my 14 elements become 14+14 =28, for the third 28+14 = 42 and so on. What is the reason for this error and how to fix it? Huge thank for the help!
Sounds like I when you start your game you’re adding to the file instead of writing a new file. You’ll have to check your code carefully to see how you initialize the data and how you read/write the data. You can easily add some breakpoints or debug.logs to see exactly what your code surrounding this system is doing and I think you’ll find it pretty quickly!
@@LlamAcademy I have already fixed this problem, thankfully. The problem caused my data saving with using List collections. I don't know why, but when i am using a List collection, the files reads wrong, that's why i got (amount of elements) + (default amount of elements) every time. I don't know why, but when i change it to simple array it started working
Hi this looks great, I am struggling a bit with saving lists of Custom classes however. In my case I have 100's of "Character" classes each with their own "Family" class reference of the Family they belong to, but then the Family class has a list with references to all the Characters that are part of the Family. I believe this is what is causing the Serialization function to throw this error: Unable to save data due to: Self referencing loop detected. How do I still serialize those references etc.? Also for serializing unity colors do I need to turn them into rgba float arrays?
Usually when you have cyclical references like this, it’s an indication that the data model design is flawed. Without knowing the details of your game, it sounds to me like “Family” would be an object that HAS multiple “people” or “Characters”, not the other way around. If you do need these references, this stackoverflow may help: stackoverflow.com/questions/35869705/handling-circular-reference-with-newtonsoft-json
@@LlamAcademy Thanks I'll take a look. How did you format the JSON at 11:40 ? I googled and found CTRL K CTRL D but Visual Studio gives an error formatting is not currently available...
Could someone please explain how to use this in a basic game not having the Demo.cs stuff? Like I have a simple player with a health int, ammo int, etc. How do I use it? Do i have to modify the existing player controller to have all new Data stuff?
In this demo class here is just showing us the "game data" that is in PlayerStats. I think I didn't do a good job explaining in this video that you need a Serializable data structure to store to the file. If your game has all your data stored on a monobehavior you have to do some work or refactoring to have a "clean" serializable class to save/load on the disk.
Hi 👋 The Aes instance that is created here: 17:21 will automatically generate an IV and Key. What I did was just copy/paste the values out from one that was generated at this time and started using those every time as those strings you see at the top of the JsonDataService class.
Hey there! Let me start out by saying AMAZING TUTORIAL! I love the way this system works and how clean and reusable the code is. I do have an issue though. While the file is being written to correctly, the data is not being retrieved correctly. For example. Let's say I have an array of weapon classes that I'm saving. The file gets written to as show below: { "Setup": { "ID": "WeaponClassSlot.1", "DisplayName": "a;sdkfja;sdlkfja;sdjf", "PrimaryWeapon": { "ID": "TestID", "DisplayName": "TestDisplayName", "WeaponRef": null, "WeaponSkins": [], "WeaponAttachments": [] }, "SecondaryWeapon": { "ID": "", "DisplayName": "", "WeaponRef": null, "WeaponSkins": [], "WeaponAttachments": [] }, "Knife": {}, "Utility1": null, "Utility2": null, "Utility3": null }, "name": "WeaponClass_Slot1", "hideFlags": 0 }, { "Setup": { "ID": "WeaponClassSlot.2", "DisplayName": "Weapon Class 2", "PrimaryWeapon": { "ID": "", "DisplayName": "", "WeaponRef": null, "WeaponSkins": [], "WeaponAttachments": [] }, "SecondaryWeapon": { "ID": "", "DisplayName": "", "WeaponRef": null, "WeaponSkins": [], "WeaponAttachments": [] }, "Knife": {}, "Utility1": null, "Utility2": null, "Utility3": null }, etc... However, when I use the load function and attempt to print the file to the console (similar to how you do with the text field) I get empty values like this: Any Idea on how I can fix this? Perhaps saving an array of a custom data set is not supported? But then that wouldn't make sense because the file gets correctly written to. It's only when trying to load the data back into an array of this data type that it returns all values as null. Thank you in advanced, and thank you for the video :) Loaded from file: [ { "Setup": { "ID": null, "DisplayName": null, "PrimaryWeapon": null, "SecondaryWeapon": null, "Knife": null, "Utility1": null, "Utility2": null, "Utility3": null }, "name": "WeaponClass_Slot1", "hideFlags": 0 }, { "Setup": { "ID": null, "DisplayName": null, "PrimaryWeapon": null, "SecondaryWeapon": null, "Knife": null, "Utility1": null, "Utility2": null, "Utility3": null }, "name": "WeaponClass_Slot2", "hideFlags": 0 }, etc...
For anyone dealing with this in the future. My issues were not having public setters. I'll leave an example below. Normally for my values I incorporate private setters to prevent cheating. The way I go about this is: public bool MyBool => _myBool; [SerailizeField] private bool _myBool = false; Removing the private setter fixed the issue and now the Json deserializes correctly
As a newcomer to coding I found this very difficult to follow because of the demo script that you don't write in this tutorial and I don't understand how to save and load the data across my other scrips in my game.
@@LordZandaurgh I sort of figured It out. You can't load and save the same JSON file in more than one script in your scene otherwise it overwrites in strange ways. Is there a particular part that's not working for you?
@@ericgardiner7715 Well I'm trying to use one script to store and alter the stats of my player and it would be convenient if I could call a function to save and load the data within that script. I'm also confused about the whole Demo script used in the video.
How I can explain it is you need two scripts: 1: that defines the JSON file that will be used for storing the data, 2: the script that gets the JSON file and creates an instance of it (Load the data), reads and edits that insane (Accessing and setting the data locally), and the replaces the JSON file with the edited instance (Saving the data). Note that I have not implemented Encryption even though I have created a bool for it. //this is what my first script is to define the JSON file is: using System; using System.Collections.Generic; [Serializable] public class CarData { public float SuspentionHeight; } //In my second script I declare the JsonDataService and the "local instance" of the data file that I defined in the previous script, as well as the unused encryption bool. using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class CarCustomiser : MonoBehaviour { //Declare Data saving variables private IDataService DataService = new JsonDataService(); private CarData CarData = new CarData(); private bool EncryptionEnabled; void Start() { //load data in start CarData = DataService.LoadData("/Car-Data.json", EncryptionEnabled); } //this is how I read the data void ReadData() { float height = CarData.SuspentionHeight; } //this is how I set a new value on the "local instance" void SetData() { CarData.SuspentionHeight = 5; } //this is how I save the data by writing over the JSON file public void SerializeJson() { if(DataService.SaveData("/Car-Data.json", CarData, EncryptionEnabled)) { } else { Debug.LogError("Could not save file."); } } } I hope that helps, or that you managed to figure it out in the meantime.
How would you go about merging different classes into the same json? like for example you used PlayerStats, how would you then add into the same file for example classes like BuildingData and EnemyStats etc etc?
I would use separate files. This data sounds unrelated and probably needs to be handled by different classes. If it absolutely must be in the same file, I would make a new class with properties EnemyData, BuildingData, and PlayerStats and serialize / deserialize this class. I highly discourage that route unless there is no other option.
@@LlamAcademy Well most games have a single save file is why. That's kind of what I am aiming for as well. Maybe some sort of way to write to the file with Header text or symbols that break up the file into different strings upon deserialization? so you'd have a string for PlayerStats which is everything under the // PlayerStats header etc? similarly for // EnemyData ?
I could also make a folder that contains jsons for all the data seperately for each savegame. But I feel that would look a lot less elegant inside the File Explorer... (Say a player wants to do some save-editing?)
Yeah. Assuming each of those is a class that holds data about each thing, then just create a class that has a property of each of those classes and serialize that one instead. Like public class SaveData { public PlayerStats PlayerStats; public EnemyStats EnemyStats; public BuildingData BuildingData; …etc } And save / load from that
Greate video,but how about saving data like: public class Item:ScriptableObject { public Sprite icon; public string name; } It went wrong when i try to save Sprite and load abstract class
Lets say you can create multi characters or even a online co-op host/client based game, can you name the file name dynamic? Like instead of the /player-stats.json you have, could you do /.json?
of course! In online games you frequently allow a player to change their name, so maybe the unique identifier for the player would be a better choice than their name though.
I'm getting the wrong key or iv error. I generated one using the commented out line and copy pasted it to my const KEY and IV. It saves the file but when i try to load the content, it gives me a Null output. Help!
This sounds like when saving or loading it is not receiving the Key or IV and a new instance with an automatically generated key and/or IV is being used for at least one of these operations.
If I understand correctly, this does not save monobehaviours. I have 100 gameobjects with multiple monobehaviours, including an AI for each gameObject (which is a finite state machine containing multiple classes). I am very confused on how I should proceed with saving.
I would generally advise against trying to serialize the full state of your game including all game objects, monobehaviours, etc... The save size will become unreasonably large. Instead, I would recommend to save the core pieces of data you need and when loading, respawn objects into the game and set up your monobehaviours from the save file. For example if you have some enemy AI, most likely they are from prefabs. When you save, you can save the location and core stats such as ammo, hp, energy, position, and rotation for each enemy. When you load, you spawn the prefabs and apply the data from the save file. This way you don't need to save the (same, repeated) full prefab structure for each enemy AI.
Hi, I managed to successfully encrypt data and be able save it onto a file but whenever I try to load the encrypted file it gives me, "CryptographicException: Bad PKCS7 padding. Invalid length 53" which happens precisely at "string result = reader.ReadToEnd(); " I have the Key & IV auto-generated and set in variables. I don't know what I might be missing...
When I had that happen it was because I just was manually tweaking a Key/IV. If I let C# generate the Key/IV, write them to the console, then copy/paste those into variables and only use those from that point on, it was always good to go. Is that the process you did for this?
@@LlamAcademy Yes, that's the process I followed. I figured out what was missing, not closing the stream after encryption made decryption problematic. So all I was missing was a stream.Close(). Thanks for replying.
The data can be serialized like what's done here. For the references, when the objects are constructed via Prefabs or Instantiate() they should grab the references to one another. It's generally not a good idea to try to serialize all of the components of all of the objects in the entire scene.
i only change 1 or 2 variables at a time but when I resave it the other variables that has been changed will reset to it defaults, how to save the new data but not changing other data?@@LlamAcademy
The most common issue I had with keys/IVs not working was an inconsistent use of them. Meaning on a write one set was used and on a read another was used. I think I mentioned in the video that to get these I made C# generate some valid key & IV then I copied those and used it. When I tried to manually make up some they didn’t work.
I think you may need to update your scripting API Compatibility level in the Project Settings > Player > Other Settings > Configuration > API Compatibility Level and set it to .NET Standard 2.1
@Hoang-nn8ot the “using” syntax used here is a newer c# feature. You can use the older syntax as well to get the same result learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/using
Great tutorial but you have one problem with the way you did it, it will only work with script that do not inherent from monobehaivor or any kind of inherent
@@LlamAcademy In the Unity Documentation, it doesn't appear that Nintendo switch has a Application.persistentDataPath defined. Is this the case? I'm currently making a Level Progress Json file that gets read from and saved to (to track high scores/bonus missions completed/etc). Any help/tips appreciated!
I would change 2 things. 1) Don't save whole class, just data you need. Saving whole class can be heavy and its problematic when you change something (backwards compatibility). 2) AES is overkill to encrypt save. Key can't be securely stored anyway - you have to decrypt data client side. Simple XOR operation is much faster and do scrambling job just fine. Edit: one more thing. JsonUtility is one of the fastest parsers and create smallest amount of garbage. Source: "JSON Libraries Comparison in Unity 5.5"
Yeah but xor doesn't read arrays and list back which AES does. I always have issues with xor. It could be in many ways unstable. For the key problem you just can encrypt byte itself to keep it safe
While this is really well done, SERIOUSLY would be nice if you could slow down any time you are using intellisense to make connections/corrections on the errors. I have to slow the video down to 25% play speed to get a chance to see what option you clicked. Several parts where you just flew through like bat shit crazy on critical info... but then took your time talking forever on misc stuff I don't need to know day to day.
@@LlamAcademy yep just a second to pause it, also thank you for replying. Additionally IdataService and JsonDataService need to be attached to something available in every scene correct? cause I am getting errors when trying to attach it as a component to my Game Manager. Saying needs to derive from monobehavior for jsondata, and then can't be abstract for idata
@thatguy9668 no, these are not MonoBehaviors so you don’t need to attach them to a GameObject. You can use them like any standard C# class. If you want to use them in the game manager you can just instantiate an instance and you are good to go. The full project is on GitHub, so you can check out how it was done in the Demo.cs MonoBehavior github.com/llamacademy/persistent-data/
Great Tutorial, you can teach me for design plain Model with your json. i can't deserialize my plain Model ; this is my plain model public class PlayerStat { [JsonProperty("Health")] public long Health { get; set; } [JsonProperty("Name")] public string Name { get; set; } [JsonProperty("BaseAttackSpeed")] public long BaseAttackSpeed { get; set; } [JsonProperty("Level")] public long Level { get; set; } [JsonProperty("HasUnlockedSomething")] public bool HasUnlockedSomething { get; set; } [JsonProperty("HasUnlockedSomethingElse")] public bool HasUnlockedSomethingElse { get; set; } public Dictionary Equipment { get; set; } [JsonProperty("Inventory")] public Inventory[] Inventory { get; set; } } stuck it can't deserialze dictionary "JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[LabJSON.Model.Slot,LabJSON.Model.Equipment]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array."
From what I read here, it seems that you have stored some data in an array and are trying to deserialize it into a Dictionary. To resolve that, whenever you are saving the data of "Equipment" make sure it is stored in a key-value format.