NullReferenceException when accessing child node of child scene (C#)

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By a_neobum

Hello!

I’m a beginner who’s feeling really stupid right now.

I have a scene HexTile (Node2D) with a child Label (Label), added through the scene editor.
I have another scene Board (Node). Board is meant to be the main scene.

Each scene has a C# script.

Board instances, and adds as children, several HexTile scenes through its script and stores them in a collection.
It then attempts to access the Label of each HexTile and set its text.
The Label is not found; the debugger claims “object reference not set to an instance of an object”.

The relevant code looks something like this:

public class Board : Node
{
    private HexTile[] _hexes;
    
	public override void _Ready()
	{
		_hexes = new HexTile[10];
		
		for (int i = 0; i < 10; i++)
		{
			var hex = new HexTile();
			AddChild(hex);
			hex.GetNode<Label>("Label").SetText("test");
		}
	}
}

I’ve confirmed that the HexTiles get properly added as child scenes of Board as, without the call to Label, they get placed and drawn properly.

I’ve confirmed that Label is properly added to HexTile, as it displays properly when running the HexTile scene in isolation, and accessing Label and editing its text works.

I get the same error if I move the Label-accessing line to the script of HexTile and run the project with Board as the main scene. (HexTile still runs fine in isolation.)

 

I cannot, for the life of me, figure out what I’m doing wrong here. Please help me!

Thank you in advance!

Is your Label a part of the Hex scene, or dynamically instantiated from the Hex _ready()?

_ready is almost definitely not called by the time you call hex.GetNode. I haven’t dug into when _ready gets called, but I’ve encountered similar problems when duplicating nodes - where AddChild doesn’t synchronously trigger _ready on the child, and instead it was getting called (I assume) on the next frame.

MitchReidNZ | 2020-02-03 04:08

:bust_in_silhouette: Reply From: Teln0

It seems that when you create your new HexTile in your script, it has no Label node attached to it, so when you try to get one, there’s nothing to get, and it returns null here:

hex.GetNode<Label>("Label")

Then you try to call the SetText() method on this object and it throws a NullReferenceException, because this object is null, there’s no Label object to call SetText() on.
Try adding Label node to your HexTile when you create it.

Yes, I know. This is what is happening. I’m trying to figure out why it is happening.

I’ve celarly stated that:

I have a scene HexTile (Node2D) with a child Label (Label), added through the scene editor.

and

I’ve confirmed that Label is properly added to HexTile, as it displays properly when running the HexTile scene in isolation, and accessing Label and editing its text works.

a_neobum | 2020-01-26 20:49

:bust_in_silhouette: Reply From: Jason Swearingen

I think you mean to use the this.FindNode() function, not AddChild().

If AddChild is correct (read the docs on it), I would guess that the child hex’s _Ready() isn’t being called during the AddChild(hex); call. Maybe consider inversion of control, so have the child register back up to it’s parent.

I do not mean to use this.FindNode() instead of AddChild(). I do not have the functionality of these two functions confused. I’d recommend that you read the docs on both these functions.

The AddChild() function is used to add a node to the calling node as a child. It is the equivalent of using the “Add Child Node” dialogue accessed from the Scene tab.

The FindNode() function is described as doing:

Finds a descendant of this node whose name matches mask as in String.match (i.e. case sensitive, but ‘*’ matches zero or more characters and ‘?’ matches any single character except ‘.’). Note that it does not match against the full path, just against individual node names. If owned is true, this method only finds nodes whose owner is this node. This is especially important for scenes instantiated through script, because those scenes don’t have an owner.

Its return type is Node. Based on this, and the description above, we can assume that FindNode() is a way of allowing a user to get access to child nodes whose full name and/or data type are not known. While nice, I do not believe that it is relevant to this scenario because both the data type and full name of the Label node are known, so the GetNode() function is more appropriate and its use wouldn’t necessitate a type conversion, as FindNode() would.

Furthermore, the this keyword is redundant as its purpose is to clear up naming ambiguities around overshadowing. I am not overshadowing FindNode() anywhere.

It would not make any sense to attempt to retrieve a node from a scene tree when attempting to add a node to that scene tree. AddChild() is correct and _Ready() is being called because, as I clearly stated:

I’ve confirmed that the HexTiles get properly added as child scenes of Board as, without the call to Label, they get placed and drawn properly.

a_neobum | 2020-01-26 21:21