Dictionary not recognising key?
I am creating a word game where you enter words letter-by-letter and then hit a "send" button to submit that word. When you hit "submit" the Game Manager checks to see if the word is in the dictionary and if it is then you get the points from the word. If not then it won't let you send the word. My issue is that I have created a variable called "wordBeingMade" which seems to work fine as a string until it is required to be checked against the dictionary. For some reason when I run the CheckWord method it says that the value of "wordBeingMade" is null (nullReferenceException). So somewhere between creating the word using CreateWord and clicking the button to run CheckWord it is being nullified. But I just can't seem to find out where that might be...?! Any help would be greatly appreciated. Code is below:
//Game Manager Code
//sets up an instance of the GameManager
public static GameManager instance = new GameManager();
//a list to store all of the values from the text file in
private List<string> dictionaryList = new List<string>();
//this sets up the field for which to add the external dictionary txt file
[SerializeField] private TextAsset dictionaryTxtFile;
//this is the string to which the full dictionary is assigned before being split up by the Split method
private string fullDictionary;
//this is the textbox that displays the current word being made
[SerializeField] private Text inputText;
//create the dictionary to store all of the words in
Dictionary<string, int> dictionary = new Dictionary<string, int>();
void Start()
{
//add the list of words from an external txt file via the inspector
fullDictionary = dictionaryTxtFile.ToString();
//get the dictionary list of words and split them on every comma
dictionaryList = new List<string>(fullDictionary.Split(','));
//add each of the words within dictionaryList to the actual dictionary itself
for (int i = 0; i < dictionaryList.Count; i++)
{
dictionary.Add(dictionaryList[i], 1);
}
}
public void CheckWord()
{
//check to see if the word is in the dictionary. If it is then clear the word being made and add points, etc.
if (dictionary.ContainsKey(wordBeingMade))
{
Debug.Log(wordBeingMade + " is a word");
}
else
{
Debug.Log(wordBeingMade + " isn't a word");
}
//this handles the incoming letter from the tile which is concatenated to the current "wordBeingMade"
public void CreateWord(string theLetter)
{
wordBeingMade = string.Concat(wordBeingMade, theLetter);
}
}
//in a separate script file for the letter tiles that you click I have...
public class Tile : MonoBehaviour
{
public Button buttonComponent;
public Text letter;
public Text points;
public List<string> letters = new List<string>() { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
public void HandleClick()
{
GameManager.instance.CreateWord(letter.text);
gameObject.tag = "Selected";
}
}
If you get a NullReferenceException
in the CheckWord
function, then, the problem does not come from wordBeing$$anonymous$$ade
. The problem is that dictionary
is null, which is pretty strange since you initialize it when declaring it. If you get a ArgumentNullException
, then it means that wordBeing$$anonymous$$ade
is null. I don't see anywhere the declaration of wordBeing$$anonymous$$ade
nor its initialization.
Please, provide the full stack trace.
Thanks Hellium! $$anonymous$$y apologies. I actually got the NullReferenceException when trying to assign wordBeing$$anonymous$$ade to the Text box, which is a side issue that I won't go into. However, when I simply try to show Debug.Log(wordBeing$$anonymous$$ade + " isn't a word");
or Debug.Log(wordBeing$$anonymous$$ade + " is a word");
it doesn't display a value where wordBeing$$anonymous$$ade should be in that Log. So it just appears as " isn't a word" when I hit the send button.
Where do you initialize
wordBeing$$anonymous$$ade
?When and who calls
CreateWord
? Is it a function called by the InputField in theonValueChanged
oronValidate
?
Answer by Hellium · Feb 02, 2019 at 12:06 PM
Following code tested and working
// GameManager.cs
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class GameManager : MonoBehaviour
{
//Game Manager Code
//sets up an instance of the GameManager
public static GameManager Instance;
//this sets up the field for which to add the external dictionary txt file
[SerializeField]
private TextAsset dictionaryTxtFile;
//this is the textbox that displays the current word being made
[SerializeField]
private Text inputText;
//a list to store all of the values from the text file in
private List<string> dictionaryList = new List<string>();
//this is the string to which the full dictionary is assigned before being split up by the Split method
private string fullDictionary;
//create the dictionary to store all of the words in
private Dictionary<string, int> dictionary = new Dictionary<string, int>();
private string wordBeingMade ;
public string WordBeingMade
{
get { return wordBeingMade ; }
set
{
Debug.LogFormat("WordBeingMade will change to '{0}'", value ) ;
wordBeingMade = value ;
inputText.text = wordBeingMade;
}
}
void Awake()
{
if( Instance != null && Instance != this )
{
Destroy( gameObject ) ;
return ;
}
Instance = this;
}
void Start()
{
//add the list of words from an external txt file via the inspector
fullDictionary = dictionaryTxtFile.text;
//get the dictionary list of words and split them on every comma
dictionaryList = new List<string>(fullDictionary.Split(','));
//add each of the words within dictionaryList to the actual dictionary itself
for (int i = 0; i < dictionaryList.Count; i++)
{
Debug.Log(dictionaryList[i]);
dictionary.Add(dictionaryList[i], 1);
}
}
public void CheckWord()
{
//check to see if the word is in the dictionary. If it is then clear the word being made and add points, etc.
if (dictionary.ContainsKey(WordBeingMade))
{
Debug.Log(WordBeingMade + " is a word");
}
else
{
Debug.Log(WordBeingMade + " isn't a word");
}
}
//this handles the incoming letter from the tile which is concatenated to the current "wordBeingMade"
public void CreateWord(string theLetter)
{
WordBeingMade = string.Concat(WordBeingMade, theLetter);
}
}
// Tile.cs
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class Tile : MonoBehaviour
{
public Button buttonComponent;
public Text letter;
public Text points;
public List<string> letters = new List<string>() { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
public void HandleClick()
{
GameManager.Instance.CreateWord(letter.text);
gameObject.tag = "Selected";
}
}
//Dictionary.txt
HELLO,WORLD
The Send
button calls GameManager.checkWorld
when clicked, each letter button calls Tile.HandleClick
where Tile
is the script attached to the button itself
Excellent! Thank you so much for your perseverance. I have been stuck on that for quite some time. Just so I am clear on what exactly was causing the issue: was it the way I had initialised the Game $$anonymous$$anager instance? Or was it more to do with the getting and setting of WordBeing$$anonymous$$ade? This code definitely works, but I'm not sure I fully understand what I was doing wrong. Thanks again. Super happy :)
It may be because the Singleton wasn't correctly implemented. $$anonymous$$onoBehaviour should not be instanciated using new
. Because of this Game$$anonymous$$anager.Instance.CreateWord
was surely calling the "badly created" Game$$anonymous$$anager while CheckWord
would be called on the correct component.
Ah ha! Okay, lesson learnt. Thanks again for your help.