I can somewhat understand the logic behind the code for ExploreNeighbors() after watching the video several times. But I don’t think I could have ever, or can ever come up with a right solution if it isn’t right in front of me. I have a couple of questions.
At 5:53, Gary types:
List neighbors = new List();
Why is the List’s ElementType a Node? Looking at the node script it takes two types of parameters:
“Vector2Int coordinates” and “bool isWalkable”. How do we know that the List is taking only the Vector2Int and not the bool? How do we know that the List will not take both? What’s the rationale behind this?
At 6:50, Gary types:
Vector2Int neighborCoords = currentSearchNode.coordinates + direction;
Also, currentSearchNode.coordinates is not a list. From what I’ve understood so far, it’s a single object with x and y coordinates. What’s the order of operations when we add something that’s not a list into a list? Is this possible with arrays and dictionaries as well? Can it be multiplied / divided as well so long as it has numerical value?
At 8:14, Gary types:
neighbors.Add(grid[neighborCoords]);
From my understanding, we are adding the neighboring coordinates into the Dictionary grid. But neighborCoords only give coordinates – there is no value associated with to the key (coordinates). Can a dictionary not have a value and still function? Or did I misread something?
Don’t quite understand what you mean with ‘How do we know the list is taking only the Vector2Int’. The list doesn’t take a Vector2Int, it takes a Node. The node holds the coordinates (Vector2Int) and the walkability (bool) of the cell in question
We are not adding anything to a list. We are adding the direction (Vector2Int) to the coordinates (Vector2Int) of the node we want to get to and putting that result in a variable called neighborCoords (Vector2Int)
We are not adding the neighboring coordinates into a dictionary, we are adding it to the neighbors list we defined at the top of the method.
You are going to have to ask me loads more questions because I don’t think you quite understand any of the code here - not putting you down, just stating an observation. Go ahead and ask. I will help as much as I can. We will have you breezing through this in no time
void ExploreNeighbors()
{
// Here we create a list of Nodes that will hold the
// node of each of the currentSearchNode's neighbors
List<Node> neighbors = new List<Node>();
// At the top of the class we defined an array of
// directions: right, left, up, and down
// Here we will loop through each of those directions
foreach(Vector2Int direction in directions)
{
// Here we create a variable that points to coordinates
// from the currentSourceNode in the direction we are
// currently looping with. If we are at (4,2) and the
// direction is 'right' (1,0) we will get (5,2) in the
// variable neighborCoords
Vector2Int neighborCoords = currentSearchNode.coordinates + direction;
// Now we check if this neighborCoords exist in the grid
if(grid.ContainsKey(neighborCoords))
{
// It does, so we retrieve the NODE from the grid and
// add it to the list we created at the top of the method
neighbors.Add(grid[neighborCoords]);
}
}
// Now we are done with the loop and have a list of valid
// neighbor nodes for the currentSourceNode
// We loop through each of these valid neighbors
foreach(Node neighbor in neighbors)
{
// If we haven't been to this neighbor yet, and we can
// walk on this neighbor
if(!reached.ContainsKey(neighbor.coordinates) && neighbor.isWalkable)
{
// we 'connect' the neighbor to this currentSourceNode
neighbor.connectedTo = currentSearchNode;
// add it to the dictionary of nodes we have reached
reached.Add(neighbor.coordinates, neighbor);
// and set it up to be checked at some point
frontier.Enqueue(neighbor);
}
}
}
Hey, thanks again for your reply!
I’m glad that you answered because after reading your reply I realized I was much more ignorant about the code than I initially thought. It would have been bad if I went in further into the lectures without completely understanding the nuances of the code.
I understand that the line takes a Node, but the Node Script itself takes two parameters, which are “Vector2Int coordinates” and “bool isWalkable”. So my initial assumption was that because Node itself holds a Vector2Int and a bool, we would have to use a dictionary instead of a list. Can lists inherently hold multiple variables? Or is it possible because it is referring to a Node and node is just one variable that refers to two variables?
Sorry, let me ask in a different way. I was just confused that despite having different data structures (adding Vector2Int Variable with a Vector2Int Array), it worked by adding the variable to each array variable. I would have expected that currentSearchNode.coordinates would have been added INTO the array.
What I expected:
neighborCoords = [Vector2Int currentSearchNode.coordinates, Vector2Int.right, Vector2Int.left, Vector2Int.up, Vector2Int.down]
What came out:
neighborCoords = [ Vector2Int currentSearchNode.coordinates + Vector2Int.right, Vector2Int currentSearchNode.coordinates + Vector2Int.left, … , …]
Is it a safe assumption for me to make that you can only add or remove items on an array or list by using dot operators such as .Add() or .Remove(), and combining variables will “amplify” the values? From all the googling I’ve done this seems to be the case so far.
I see what you mean now. I understand that we are adding to the neighbors list with .Add() operator. Here is another question to ensure that I understand – The List “neighbors” is taking a Node, which is supposed to hold Vector2Int and a bool. In the line
neighbors.Add(grid[neighborCoords]);
grid[neighborCoords] only has the coordinates, but not the bool. But in your reply you specifically state that we are grabbing the NODE, which means I am not understanding where the bool is – could I ask you where the bool is coming from?
The list doesn’t hold multiple values and it can’t. Node is a class (type) and the list only holds this type. The coordinates and isWalkable are members of the Node type and are therefore part of the Node. It’s the same way x and y are part of a Vector2Int.
Yes, you almost have it right. Like I said above, these are members of Node.
This is almost right. neighborCoords just holds the calculated result for 1 specific direction. With each loop iteration, we change the direction to the next one in the array and then recalculate neighborCoords. This doesn’t matter much because we have taken the result of the previous calculation and retrieved the Node that corresponds to that coordinate from the grid and stored a reference to it. Once we did that, we no longer needed the calculated value and it was ok to move on to the next direction and calculate the next neighbor’s coordinates.
Not quite. You can add and remove items with .Add() and .Remove() from lists, but arrays need to be declared with a size. Once declared, each element within that will hold the default of the type of the array. Items cannot be added or removed from an array. Once the size is defined, that is what it is. That is unless we declare the array in the same way we did directions
This defines an array of Vector2Int. It gets initialised with 4 values; Vector2Int.right, Vector2Int.left, Vector2Int.up and Vector2Int.down. This array now has a size of 4 (because we initialised it with 4 values) and can only hold 4 items of type Vector2Int. We cannot add or remove items from it. We can only change the items.
Yes, kinda. Again, Node is a type and coordinates and isWalkable are members of that type.
grid is a dictionary. It, too, holds the Node but this time it has a key that will make it quicker to find. The key is the coordinates. I’m not sure if I need to explain a dictionary so I will, to keep the bases covered;
A dictionary is like a shelf full of boxes. Each of these boxes have a little label on it. Now, when we created the grid, we put a Node in each box and wrote the coordinates for that node on the label. With grid[neighborCoords] we are asking the dictionary to retrieve the Node from the box with the label that matches neighborCoords. So we get the Node back. The node holds its coordinates and its isWalkable boolean.
Thank you so much. I’ll mark above as the solution because it pretty much answers almost everything for me. I still have a question on 3) though.
Got it! I understand what you mean now. The Vector2Int analogy was perfect. Thanks for letting me know of the nuance between a variable and a member of a variable. It’s probably been repeated multiple times in the lectures already but I just now realize how important it is. I will keep it to heart.
I see where I went wrong. That makes total sense on why you didn’t understand my question to begin with, because there was only one coordinate at a given time. Even worse, I extrapolated that into arrays and confused myself for it, when the most prominent property of an array is that the size is fixed unlike a list. Thank you for correcting me.
If I’m understanding right, you are saying that the nodes were created when we made the
Dictionary <Vector2Int, Node> grid;
One member of the node, the coordinates, are edited via ExploreNeighbors() method.
The other member of the node, the isWalkable boolean, is not edited and therefore unaffected. We have the same boolean value for every coordinate (unless we choose to edit it in the inspector). Do I have it right?
grid comes from the GridManager and is created by the GridManager. The code in GridManager that creates the grid is
void CreateGrid()
{
for(int x = 0; x < gridSize.x; x++)
{
for(int y = 0; y < gridSize.y; y++)
{
// Make the box label
Vector2Int coordinates = new Vector2Int(x,y);
// add it to the boxes: Add(label, contents)
grid.Add(coordinates, new Node(coordinates, true));
}
}
}
I have commented with the original ‘shelf of boxes’ analogy
When the PathFinder script got loaded we got the grid from the GridManager in the Awake() method
void Awake()
{
gridManager = FindObjectOfType<GridManager>();
if(gridManager != null)
{
grid = gridManager.Grid; // <- Here we are getting the grid dictionary from GridManager
startNode = grid[startCoordinates];
destinationNode = grid[destinationCoordinates];
}
}
We actually do not edit any coordinates in ExploreNeighbors() but it does start to look like you are getting the hang of it. Apart from the ‘editing’ you have everything correct here. We only set the connectedTo (which is another member of Node)
If you think there’s editing happening, feel free to tell me why and I’ll try to explain
In retrospect, I don’t know why I thought members of the node was getting edited when clearly there was nothing in the pathfinder script that did any editing. We search the neighbor of the currentSearchNode (create list beforehand for it), we iterate through the loop and only add the neighboring coordinates if it exists. No edits.
I think it’s just a beginner’s brain not being able to internalize everything I’ve learned properly yet, and confusing the wording of the variables on what I determined it to be (which sounds really foolish but I did not realize it back then).
Looking at the beginning of my question I realize how difficult it was for someone to understand what I was trying to say. Thank you for taking your time to break down everything for me. You probably saved me countless hours of trying to troubleshoot. I feel a lot more confident in my understanding of the code now.
It is a lot to take in. I am actually quite glad I am not a beginner anymore because there is so much to learn. And each lecturer (and community member) have their own style of coding and because there are so many ways to do a single thing it can get very confusing if everyone is doing it in their own way.
Good luck with the rest of the course and don’t hesitate to ask more questions. There are many brilliant community members on here that will be glad to help you out and who knows, maybe one day you will be helping other beginners too