I am trying to save values to a dictionary by pressing different gameobjects. Each gameobject have a unique value that I set in the inspector. I haven’t used the dictionary before so I’m quite n00b when it comes to understanding how to implement it and right now it saves a value, but then overwrite that value when I click on another object. How do I make it add to the dictionary without removing the old value?
PS I am using Save Game Free to be able to save the dictionary over time and between sessions.
This is what I put on each GameObject and then the “letter” value is unique for each and set in the inspector.
//Save to dictionary
Dictionary<string, int> letterList = new Dictionary<string, int>();
letterList.Add(letter, 1);
SaveGame.Save<Dictionary<string, int>>("letters", letterList);
Do I have to make like a controller object with a public dictionary on it instead? Or how do I go about achieving the result I want?
They are called ‘Dictionary’ for a reason - you can look up you value under a certain key.
So a dictionary works that you group a key with a value to save (they call this key-value-pair, not an inspired, but at least apt, name). Lets say you have a player’s game object for Peter, and another for Jane. You could save and acces them using their names as follows:
save:
Dictionary<string, GameObject> players;
// save some values for Peter and Jane
players["Peter"] = theGameObject;
players["Jane"] = theOtherGameObject;
You’d then access Jane’s object like this
GameObject currentPlayer = players["Jane"];
So, to (finally) answer your question: you to change the key to save to a different ‘slot’ in the Dictionary. You currently save everything under the ‘letters’ key.
Sorry Franz, I posted before I had read your answer. So then I don’t need to have a controller, I can do this all on each gameobjects code like I tried from the beginning? And I don’t have to call the dictionary by name to access it’s values, I can just access the value by calling the string?
Because what I want to do is when leaving this scene and opening another I want to set gameobject active (by running through the dictionary) depending on which ones the player found in the last scene, AKA the letters of the alphabet. So if he found A, Z and X in scene 1 these should be visible in scene 2
You are opening a new can of worms if you want the data to persist between Scenes - because unless told differently Unity forgets all scene data and variables. If you really, absolutely need to remember this data between saves, re-read the documentation about ‘DontDestroyOnLoad’ and how to use a ‘Singleton’.
The idea is that you know where the code failed, and you then replace the ‘Debug.Log()’ with some code that gracefully handles this Situation, and then continue processing.
Well that is why I use the SaveGame.Save, it’s an asset that saves values to JSON so it can be remembered and also read from in other scenes.
I want to be able to call the main dictionary and run through the values. Like this:
letters = SaveGame.Load<Dictionary<string, int>>("letters");
if (SaveGame.Exists("letters")) {
foreach (KeyValuePair<string, int> l in letters)
{
Debug.Log(l);
}
}
It works fine for the one value that got overwritten, I just don’t understand how to make it so that it adds more values to the same dictionary. I guess it’s because I’m not calling the dictionary, I am calling the value?
I’m not entirely sure what you mean by “I’m not calling the dictionary, I am calling the value” - but I’m sure you mean the correct stuff
You seem to be using some third-party utility which makes it difficult to Diagnose what is going on. There are great tools available on the Asset Store (for free) that can convert a Dictionary into a JSON string, and you can then save that string using PlayerPrefs.
Note also in your example above, you should test Exists() before Loading it (otherwise it could fail before you test).
The reason that it gets overwritten is that you do not provide different keys (names) for the save slots. Then again, perhaps you don’t need to if you players continue to add/discover new letters in the subsequent levels.
Hmm well what I mean is that I want to loop through the saved JSON dictionary and see what letters are found and which ones aren’t. Here is the example for how to save to the asset:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BayatGames.SaveGameFree;
public class SimpleUsage : MonoBehaviour {
void Start () {
Dictionary<string, int> playerScores = new Dictionary<string, int> ();
playerScores.Add ( "John", 100 );
playerScores.Add ( "Jack", 200 );
// Saving the data
SaveGame.Save<Dictionary<string, int>> ( "playerScores", playerScores );
}
}
As I understand this you first create a dictionary, then add values to that and then save that dictionary as “playerScores” and it should then hold all the values.
So I did like this which in my mind is the same:
Dictionary<string, int> letterList = new Dictionary<string, int>();
letterList.Add(letter, 1); // Letter is a unique name set in inspector
SaveGame.Save<Dictionary<string, int>>("letters", letterList);
And by calling “letters” dictionary from another gameobject it should loop through the values. But it just shows the value of the latest pressed gameobject, “A, 1” for instance. So it has to be something like what you are saying, that I overwrite the value. Maybe I have to call the old values before saving so they are added to the same list?
Perhaps it would be a good idea if you took a step back, and a Piece of paper and then put down:
what data (e.g. letters), will be saved where, how and when.
Meaning:
what data do you need to save, and why.
when will you need to acces it.
what happens to data that you accessed, and subsequently changed
can there be different set of data that need to be accessed independently? (as an example, many games have different saves per player, and each player can have multiple save slots)
Once you put that down, you should try to put that into such words that even idiots (i.e. me) can understand it. Implement it, and if it doesn’t work, Show the code along with the explanantion of what you are doing (see above), so we can understand what is going on.
You need to realize that Dictionaries are much less smart than you probably imagine. If you define a Dictionary to be of type
Dictionary<string, int> theDict
It will be exactly that: for each key there will be exactly one int. So if you did the following
theDict.Add("Test",1);
theDict.Add ("Test",2); // this will fail with Add as it produces an exception
theDict["Test"] = 2; // this will work, overwriting the previous value 1
When you then Access the value of theDict[“Test”], youll receive a single value: 2
That is how Dictionaries work. Do you really want to store a single int under a key (name), or do you want to mayhaps store a list of ints under a key? If so, you need to rewrk the Definition of the dict.
Yes well I am just trying to assign the letters of the alphabet with the int 1 to the dictionary as you go about finding them. So if you’ve found A, B and C they will be added to the dictionary with value 1 next to them, indicating they are found. And then save that.
That way I can call the dictionary and see which letters have been found by checking if their value is 1. Have A and B been found? Then A and B is unlocked in other scenes.
Perhaps you should investigate using a List instead (or even List). You simply Add new letters (as strings) to the list as they are discovered, and you can check by using the ‘Contains()’ method, e.g.
List<string> myList = new List<string>();
myList.Add("A");
myList.Add("C");
myList.Add("X");
// list now has A, C, X
// see if list contains "A"
if (myList.Contains("A")) {
... do stuff ...
}
Oooh! yes this would be much easier. But I would still probably run into the problem that I am overwriting the list each time I press an object. I think I have to grab the old values somehow each time I press the object and then add them to the list before saving it with SaveGame. But I think I have a plan for this. Trying to execute it now.
PS to figure this out I am trying to grab a script from each prefab in a list but the script isn’t being discovered. Can I not grab a script with getcomponent in a foreach loop from a prefab?
GameObject[] a = FindObjectOfType<AR_spawnRandom>().alphabet; //Get all the findable objects of type alphabet
if (a == null)
{
Debug.Log("did not find the list with all the objects");
return;
}
foreach (GameObject l in a)
{
l.GetComponent<AR_catchAlphabet>(); //Get the script attached to each of the gameobjects in the list
}
Error: he type or namespace name ‘AR_catchAlphabet’ could not be found (are you missing a using directive or an assembly reference?)
But it is attached to each gameobject in the list.
I figured it out Franz! With major help from you even though I think you found me a bit messy in my explanations haha. Although I use the name of the prefab instead of the script with the coded variable I would like to have used, because I couldn’t get that bit working.
I learned a lot today. I hope one day I know so much that I can contribute helping others here paying it forward. Here is code for future referens:
SavedDataManager
//If first start, loop through all objects to be found and set their value to zero, save it to dictionary "letters" in SaveGame
Dictionary<string, int> letterList = new Dictionary<string, int>(); //Set up new dictionary
GameObject[] a = FindObjectOfType<AR_spawnRandom>().alphabet; //Get all the findable objects of type alphabet
if (a == null)
{
Debug.Log("did not find the list with all the objects");
return;
}
foreach (GameObject l in a)
{
letterList.Add(l.name, 0);
//l.GetComponent<AR_catchAlphabet>(); //Get the variable letters from the script attached to each of the gameobjects in the list
}
SaveGame.Save<Dictionary<string, int>>("letters", letterList); //Save the dictionary
Dictionary<string, int> dic = SaveGame.Load<Dictionary<string, int>>("letters");
foreach (KeyValuePair<string, int> o in dic)
{
//Debug.Log(o);
}
}
else
{
//If not first start, do nothing
Debug.Log("not first start");
}
Code on object:
//Save to dictionary
Dictionary<string, int> letterList = new Dictionary<string, int>();
letterList = SaveGame.Load<Dictionary<string, int>>("letters");
letterList[letter] = 1; //Adds new value to the letter pressed
foreach (KeyValuePair<string, int> l in letterList)
{
Debug.Log(l);
}
SaveGame.Save<Dictionary<string, int>>("letters", letterList);