Search Unity

  1. The 2022.1 beta is now available for testing. To find out what's new, have a look at our 2022.1 beta blog post.
    Dismiss Notice

Null reference headache

Discussion in 'Scripting' started by Crawdad, Jan 14, 2022 at 8:42 PM.

  1. Crawdad

    Crawdad

    Joined:
    Nov 23, 2016
    Posts:
    68
    Hey, guys. Once again, I'm coming hat-in-hand hoping someone can set me straight on some code. I'm trying to set some variables in a list of classes. For some reason, I'm getting a null reference exceptions on lines 28-30 of the NodeManager script. The lists are both populating, and I'm just not seeing what's going wrong. Advice would be appreciated.

    Node Manager:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEngine;
    5.  
    6. public class NodeManager : MonoBehaviour
    7. {
    8.     public List<GameObject> objectList;
    9.     public Node[] nodeList;
    10.     int identifier;
    11.     void Awake()
    12.     {
    13.         foreach (Transform child in transform)
    14.         {
    15.             objectList.Add(child.gameObject);
    16.             child.GetComponent<NodeCost>().identifier = identifier;
    17.             identifier++;
    18.         }
    19.         NodePosition();
    20.     }
    21.  
    22.     void NodePosition()
    23.     {
    24.         nodeList = new Node[objectList.Count];
    25.  
    26.         for (int i = 0; i < objectList.Count; i++)
    27.         {
    28.             nodeList[i].thisObject = objectList[i];
    29.             nodeList[i].worldPosition = objectList[i].transform.position;
    30.             nodeList[i].identifier = objectList[i].GetComponent<NodeCost>().identifier;
    31.         }
    32.     }
    33. }
    Node Class:
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6.  
    7. public class Node : IHeapItem<Node>
    8. {
    9.  
    10.     public bool walkable;
    11.     public Vector3 worldPosition;
    12.     public List<Node> neighbors;
    13.  
    14.     public float gCost;
    15.     public float hCost;
    16.     public Node parent;
    17.     public GameObject thisObject;
    18.     int heapIndex;
    19.     public int identifier;
    20.  
    21.  
    22.     public Node(bool _walkable)
    23.     {
    24.         walkable = _walkable;
    25.     }
    26.  
    27.     public float fCost
    28.     {
    29.         get
    30.         {
    31.             return gCost + hCost;
    32.         }
    33.     }
    34.  
    35.     public int HeapIndex
    36.     {
    37.         get
    38.         {
    39.             return heapIndex;
    40.         }
    41.         set
    42.         {
    43.             heapIndex = value;
    44.         }
    45.     }
    46.  
    47.     public int CompareTo(Node nodeToCompare)
    48.     {
    49.         int compare = fCost.CompareTo(nodeToCompare.fCost);
    50.         if (compare == 0)
    51.         {
    52.             compare = hCost.CompareTo(nodeToCompare.hCost);
    53.         }
    54.         return -compare;
    55.     }
    56. }
     
  2. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    1,245
    This line:
    Code (CSharp):
    1. nodeList = new Node[objectList.Count];
    creates an array of Nodes with a length of objectList.Count. However it's just an array that can hold references to Node instances. However you never create a single Node instance, never. So the array you created is an array that is full of null references. You have to do

    Code (CSharp):
    1. for (int i = 0; i < objectList.Count; i++)
    2. {
    3.     nodeList[i] = new Node();
    4.     // [ ... ]
    5.  
     
    Jnsptz, Crawdad and Kurt-Dekker like this.
  3. Crawdad

    Crawdad

    Joined:
    Nov 23, 2016
    Posts:
    68
    Facepalm, of course! Thank you, sir.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    21,292
    As a point of style, unless there's a compelling reason to do otherwise, I like this approach for initializing arrays of reference types:

    Code (csharp):
    1. var node = new Node();
    2. node.foo = 1;
    3. node.bar = 2.0f;
    4. node.bat = "hello";
    5. // etc
    6. // now that node is complete and initialized, stick it in the array
    7. nodeList[i] = node;
    8.  
    I like this because:

    - typing
    node
    many times is easier than
    nodeList[i]

    - only a fully-initialized Node is ever inserted in nodeList

    YMMV
     
    Last edited: Jan 15, 2022 at 4:46 PM
    Crawdad, Bunny83 and StarManta like this.
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    1,245
    Yes, absolutely ^^. That's how I would do it for sure. Any array access is slightly more expensive compared to using a local variable. While performance is only a second cause, I also like the simpler approach as you can use a shorter name and the code is less cluttered.

    I've seen people who did something like

    Code (CSharp):
    1. someObject.gameObject.GetComponent<SomeCrazyComponentName>().someOtherObject.var1 = 1;
    2. someObject.gameObject.GetComponent<SomeCrazyComponentName>().someOtherObject.var2 = 42;
    3. someObject.gameObject.GetComponent<SomeCrazyComponentName>().someOtherObject.var5 = 66;
    Or even some that slapped a
    GameObject.Find
    in front of each line -.- Watching at such code phsyically hurts inside :)
     
    Crawdad and Kurt-Dekker like this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    21,292
    There was a great post last year where someone was doing exactly the above shenanigans and then making these weird InvokeRepeating() crazinesses (with a string method name) because they were "trying to avoid Update()."

    The palm, the face, they were one and the same.
     
    Crawdad likes this.
unityunity