Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct here to familiarize yourself with the rules and how to post constructively.

  2. Unity 2022.1 is now available as the latest Tech release.
    Dismiss Notice
  3. Improve your project's performance with our new guide on profiling in Unity.
    Dismiss Notice

does this 'key not found' error have to do with serialization?

Discussion in 'Scripting' started by Falkrons, May 3, 2015.

  1. Falkrons

    Falkrons

    Joined:
    Dec 31, 2014
    Posts:
    8
    First I am rather new and am working with C#. I have been researching this issue for a while now and have not been able to resolve myself so I bring this to the community. My brain is on the verge of exploding with the overload of information. I asked this over in the answers section but have been waiting for moderator approval and, well, I don't have days to wait on them so I am coming here too.

    I am trying to assign the instanced versions of a Prefab to a dictionary for quick access later. The problem is it seems that "later" is not soon enough for me. I have two scripts (SpawnController and TankController) on different gameObjects. The gameObject with the SpawnController on it has only one instance while the gameObjects with the TankController are many. The SpawnController has a dictionary that has information assigned during the Awake function. I then attempt to access this information on the newely Instantiated gameObject.tankController's Start function, which is where the errors seems to occur.

    KeyNotFoundException: The given key was not present in the dictionary. System.Collections.Generic.Dictionary`2[System.Int32,UnityEngine.GameObject].get_Item (Int32 key) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:150) TankController.Start () (at Assets/scripts/TankController.cs:38)

    Here are the scripts:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class SpawnController : MonoBehaviour {
    6.      public Transform[] tankSpawn;
    7.      public GameObject tank;
    8.      public int numberOfPlayers;
    9.      public  Dictionary<int, GameObject> tanks = new Dictionary<int, GameObject>();
    10.      void Awake () {
    11.          GameObject tankInstance;
    12.          for (int i = 0; i < numberOfPlayers; i++) {
    13.              tankInstance = Instantiate(tank, tankSpawn[i].position, tankSpawn[i].rotation)as GameObject;
    14.              tanks.Add (i, tankInstance);
    15.          }
    16.      }
    17. }
    TankController script:

    Code (CSharp):
    1.  using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. public class TankController : MonoBehaviour {
    5.      public GameObject manager; // gameObject reference for the SpawnController
    6.      SpawnController spawncontroller;
    7.    
    8.      void Start () {
    9.          spawncontroller = manager.GetComponent<SpawnController> ();
    10.          if (spawncontroller.tanks[0] == this) {
    11.                 //do something
    12.          } else if (spawncontroller.tanks[1] == this) {
    13.                 //do something else
    14.         }
    15.     }
    16. }

    I have verified the variables are all correct and accessible; however, the dictionary returns empty at every point until the Start function of the SpawnController script. I checked within the for loop, the Awake function of both scripts, I tried my own custom function that was called inside the for loop of SpawnController and every other place I thought useful...all returned 0 on Debug.Log (tanks.Count); until, as I mentioned the Start function of the original script where it returned correctly. I started looking into serialization and was wondering if that has anything to do with this; however before I spent many more hours trying to understand serialization I figured I would ask the community? Thank you for your help in advance!
     
  2. Deleted User

    Deleted User

    Guest

    Why use a dictionary? Seems a List would do just as well.

    Also, have you checked your " numberOfPlayers;"? If it's 0, then nothing will be added to the dictionary.
     
    lordofduct likes this.
  3. Falkrons

    Falkrons

    Joined:
    Dec 31, 2014
    Posts:
    8
    numberOfPlayers is set to 4. I cant think off the top of my head right now why a list would not work...Ill give it a try and see if it does.
    So I switched things over to a list and recieved this error instead:
    ArgumentOutOfRangeException: Argument is out of range.
    Parameter name: index
    System.Collections.Generic.List`1[UnityEngine.GameObject].get_Item (Int32 index) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/List.cs:633)
    TankController.Start () (at Assets/scripts/TankController.cs:38)

    But during start of the SpawnController the List is populated correctly, which leads me right back to the original question. I feel like I am not able to access the List/Dictionary because it is not available when I am trying to get a reference from it even though the information has been input?
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,021
    By and chance is TankController attached the prefab 'tank' that you're instantiating?

    I'm betting your code accessing the list is firing BEFORE it's even added to the list.

    Check if the list is populated in TankController... do this:

    Code (csharp):
    1.  
    2. Debug.Log(spawncontroller.tanks.Count); //if this is 0, the tanks haven't been added yet
    3.  

    Also, you're sticking GameObjects in the Dictionary or List (whichever you end up using)... but you compare to 'this' in TankController... which is a TankController. A GameObject and a TankController will NEVER equal... they just won't ever be the same object, they can't be. Maybe you mean to compare to the gameobject TankController is attached to? In that case you should be comparing to 'this.gameObject'.

    Lastly, why don't you instead in 'TankController' say (if using list):

    Code (csharp):
    1.  
    2. var index = spawncontroller.tanks.IndexOf(this.gameObject);
    3. if(index == 0)
    4. {
    5.     //do something
    6. }
    7. else if(index == 1)
    8. {
    9.     //do something else
    10. }
    11.  
    You could even use a switch statement instead...

    Code (csharp):
    1.  
    2. var index = spawncontroller.tanks.IndexOf(this.gameObject);
    3. switch(index)
    4. {
    5.     case 0:
    6.         //do something
    7.         break;
    8.     case 1:
    9.         //do something else
    10.         break;
    11. }
    12.  
     
  5. Falkrons

    Falkrons

    Joined:
    Dec 31, 2014
    Posts:
    8
    Yes it is.

    Yes, oops. Thank you, im sure I would have spent a bit of time figuring that out after I got the errors to go away. I was meaning this.gameObject and well... bool noob = true;

    So ive been fiddling. The TankController script on all the newly Instantiated tanks have the reference to the SpawnController as early as the TankController Awake function...ok...so I try and find out when the list is filled and accessible from the TankController script and it appears it never is. I added:
    to various functions within TankController and it always returns 0 even when the Awake and Start function of SpawnController is returning 4. Im confused as to why the list appears empty to each instance even though it has a reference?
    This is how the log appears if that is useful.
    errors.jpg
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,021
    can you show that with out the 'collapse'?

    So we can see the order.
     
  7. Falkrons

    Falkrons

    Joined:
    Dec 31, 2014
    Posts:
    8
  8. Falkrons

    Falkrons

    Joined:
    Dec 31, 2014
    Posts:
    8
    Soo....yeah....well I figured out what was wrong....

    after I added:
    manager = GameObject.FindGameObjectWithTag ("GameManager");

    I was referencing the prefab version of the Manager and well there was nothing in the list therefore it wouldnt work. So I added a tag and got the instance reference that way...yeah oops. thanks for the help though yall.
    supremegrandruler
    the List is working (probably much better) thank you!
    lordofduct
    the switch statement works great thank you!
     
unityunity