KeyNotFound Exception when playing.

So, i’m creating a pseudo-procedural dungeon generator for my game but when my player goes into the “Door” that should lead to the next room it gives me this error :
“KeyNotFoundException: The given key was not present in the dictionary.
System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) (at :0)
Room.Neighbor (System.String direction) (at Assets/Scripts2/Room.cs:70)
EnterDoor.OnCollisionEnter2D (UnityEngine.Collision2D col) (at Assets/Scripts2/EnterDoor.cs:18)”

Now, i want to know what i’m doing wrong, as i’m following a tutorial to generate my dungeons, so i’ll attach you the various script responsible for the whole process in the hope of gaining some kind of insight from you guys.

Dungeon Gen Script :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class DungeonGeneration : MonoBehaviour
{

    [SerializeField]
    private int numberOfRooms;


    private Room[,] rooms;
    private Room currentRoom;
    static DungeonGeneration instance;


     void Awake()
    {
        if (instance == null)
        {
            DontDestroyOnLoad(this.gameObject);
            instance = this;
            this.currentRoom = GenerateDungeon();
        }
        else
        {
            string roomPrefabName = instance.currentRoom.PrefabName();
            GameObject roomObject = (GameObject)Instantiate(Resources.Load(roomPrefabName));
            Destroy(this.gameObject);
        }
    }

    void Start()
    {
        
        string roomPrefabName = this.currentRoom.PrefabName();
        GameObject roomObject = (GameObject)Instantiate(Resources.Load(roomPrefabName));
        
    }
    
    private Room GenerateDungeon()
    {
        int gridSize = 3 * numberOfRooms;

        rooms = new Room[gridSize, gridSize];
        Vector2Int initialRoomCoordinate = new Vector2Int((gridSize / 2) - 1, (gridSize / 2) - 1);

        Queue<Room> roomsToCreate = new Queue<Room>();
        roomsToCreate.Enqueue(new Room(initialRoomCoordinate.x, initialRoomCoordinate.y));
        List<Room> createdRooms = new List<Room>();
        while (roomsToCreate.Count > 0 && createdRooms.Count < numberOfRooms)
        {
            Room currentRoom = roomsToCreate.Dequeue();
            this.rooms[currentRoom.roomCoordinate.x, currentRoom.roomCoordinate.y] = currentRoom;
            createdRooms.Add(currentRoom);
            AddNeighbors(currentRoom, roomsToCreate);
        }

        foreach (Room room in createdRooms)
        {
            List<Vector2Int> neighborCoordinates = room.NeighborCoordinates();
            foreach (Vector2Int coordinate in neighborCoordinates)
            {
                Room neighbor = this.rooms[coordinate.x, coordinate.y];
                
                if (neighbor != null)
                {
                    room.Connect(neighbor);
                }
            }
        }

        return this.rooms[initialRoomCoordinate.x, initialRoomCoordinate.y];
    }

   private void AddNeighbors(Room currentRoom, Queue<Room> roomsToCreate)
    {
        List<Vector2Int> neighborCoordinates = currentRoom.NeighborCoordinates();
        List<Vector2Int> availableNeighbors = new List<Vector2Int>();
        foreach (Vector2Int coordinate in neighborCoordinates)
        {
            if (this.rooms[coordinate.x, coordinate.y] == null)
            {
                availableNeighbors.Add(coordinate);
            }
        }

        int numberOfNeighbors = (int)Random.Range(1, availableNeighbors.Count);

        for (int neighborIndex = 0; neighborIndex < numberOfNeighbors; neighborIndex++)
        {
            float randomNumber = Random.value;
            float roomFrac = 1f / (float)availableNeighbors.Count;
            Vector2Int chosenNeighbor = new Vector2Int(0, 0);
            foreach (Vector2Int coordinate in availableNeighbors)
            {
                if (randomNumber < roomFrac)
                {
                    chosenNeighbor = coordinate;
                    break;
                }
                else
                {
                    roomFrac += 1f / (float)availableNeighbors.Count;
                }
            }

            roomsToCreate.Enqueue(new Room(chosenNeighbor));
            availableNeighbors.Remove(chosenNeighbor);
        }
    }

    private void PrintGrid()
    {
        for (int rowIndex = 0; rowIndex < this.rooms.GetLength(1); rowIndex++)
        {
            string row = "";
            for (int columnIndex = 0; columnIndex < this.rooms.GetLength(0); columnIndex++)
            {
                if (this.rooms[columnIndex, rowIndex] == null)
                {
                    row += "X";
                }
                else
                {
                    row += "R";
                }
                Debug.Log(row);
            }
            
        }

     
    }

    public void MoveToRoom (Room room)
    {
        this.currentRoom = room;
    }

    public Room CurrentRoom()
    {
        return this.currentRoom;
    }

   

}

Rooms Script :

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class Room
{
    public Vector2Int roomCoordinate;
    public Dictionary<string, Room> neighbors;

    public Room (int xCoordinate, int yCoordinate)
    {
        this.roomCoordinate = new Vector2Int(xCoordinate, yCoordinate);
        this.neighbors = new Dictionary<string, Room>();
    }

    public Room (Vector2Int roomCoordinate)
    {
        this.roomCoordinate = roomCoordinate;
        this.neighbors = new Dictionary<string, Room>();
    }

    public List<Vector2Int> NeighborCoordinates ()
    {
        List<Vector2Int> neighborCoordinates = new List<Vector2Int>();
        neighborCoordinates.Add(new Vector2Int(this.roomCoordinate.x, this.roomCoordinate.y - 1));
        neighborCoordinates.Add(new Vector2Int(this.roomCoordinate.x + 1, this.roomCoordinate.y));
        neighborCoordinates.Add(new Vector2Int(this.roomCoordinate.x, this.roomCoordinate.y + 1));
        neighborCoordinates.Add(new Vector2Int(this.roomCoordinate.x - 1, this.roomCoordinate.y));

        return neighborCoordinates;
    }

    public void Connect (Room neighbor)
    {
        string direction = "";
        if (neighbor.roomCoordinate.y <this.roomCoordinate.y)
        {
            direction = "N";
        }

        if (neighbor.roomCoordinate.x > this.roomCoordinate.x)
        {
            direction = "E";
        }

        if (neighbor.roomCoordinate.y > this.roomCoordinate.y)
        {
            direction = "S";
        }

        if (neighbor.roomCoordinate.x < this.roomCoordinate.x)
        {
            direction = "W";
        }
    }

    public string PrefabName()
    {
        string name = "Room_N";
        foreach (KeyValuePair<string, Room> neighborPair in neighbors)
        {
            name += neighborPair.Key;
        }

        return name;
    }

    public Room Neighbor(string direction)
    {
        return this.neighbors[direction];
    }



}

Enter Doors script :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class EnterDoor : MonoBehaviour
{
    [SerializeField]
    string direction;

     void OnCollisionEnter2D(Collision2D col)
    {
        if (col.gameObject.tag == "Player")
        {
            GameObject dungeon = GameObject.FindGameObjectWithTag("Dungeon");
            DungeonGeneration dungeonGeneration = dungeon.GetComponent<DungeonGeneration>();
            Room room = dungeonGeneration.CurrentRoom();
            dungeonGeneration.MoveToRoom(room.Neighbor(this.direction));
    
            SceneManager.LoadScene("Demo");
        }
    }
}

On a final note, i did add the directions to the Door game objs - and they’re correct. But to be honest i’m new to dictionaries and unity overall so i may have done something weird? I’m not asking for “Code fix” in general, but i would like to understand what i’m doing wrong, otherwise i’ll never learn. Thanks for your time.

A dictionary is a data structure that assigns a value to a specific key and stores them as a pair. Currently you are creating the neighbor rooms, but you are not adding them to your neighbors dictionary.

Thus, when entering a door your EnterDoor script is ‘requesting’ the room, for example, to “N” direction, but you never told the dictionary which of the created neighbor room should be the room assigned to “N”

For more information on dictionaries see: Dictionary<TKey,TValue> Klasse (System.Collections.Generic) | Microsoft Learn

     public void Connect (Room neighbor)
     {
         string direction = "";
         if (neighbor.roomCoordinate.y <this.roomCoordinate.y)
         {
             direction = "N";
         }
 
         if (neighbor.roomCoordinate.x > this.roomCoordinate.x)
         {
             direction = "E";
         }
 
         if (neighbor.roomCoordinate.y > this.roomCoordinate.y)
         {
             direction = "S";
         }
 
         if (neighbor.roomCoordinate.x < this.roomCoordinate.x)
         {
             direction = "W";
         }

         neighbors.Add(direction, neighbor);
     }

When scanning over your code this might fix it. The whole function was not doing anything, as you only checked in which direction the neighbor is, but you were not assigning anything

Moreover, I think it is a bad practice to use a string as an identifer for something easily definable like a direction, as it is predefined to North, East, South and West.

An enum representing these four directions would be more suitable, as it is easier to read, develop and maintain (it is easy to make a small mistake with strings, thats hard to find, like not capitalizing a letter. See also: c# - API Design: Should I use strings or an enum for dictionary keys - Software Engineering Stack Exchange )(To make it clear: this it no showstopper, I just don’t really like it :wink: )