C# - Instantiate() not returning correct class

Godot Version

v4.3.stable.mono.official [77dcf97d8]

Question

I am new to Godot so I decided to follow a bunch of youtube tutorials on how to generate a voxel landscape. This works and was made in gdscript.

I am now trying to get it to work in C#, which seems to be working when I build and run the project, but not when running it from the editor. I have an exported bool that I can check and when it is checked it calls the GenerateChunk method in a loop.

The problem is the following:

I have a scene called WorldGenerator.tscn with the root node a Node3D with a WorldGenerator.cs script attached. This script instantiates a Chunk.tscn and adds as a child to a specific node. The Chunk.tscn scene has a Node3D as root with the Chunk.cs script attached.

In the WorldGenerator script I have the following method:

private void GenerateChunk(Vector3I position)
{
	GD.Print("=====");
	chunkPrefab = GD.Load<PackedScene>("res://scenes/WorldGeneration/ChunkCs.tscn");
	GD.Print(chunkPrefab.GetType());

	var scene = chunkPrefab.Instantiate();
	GetNode("Chunks").AddChild(scene);

	GD.Print(scene.GetType());

	var scene2 = chunkPrefab.Instantiate<Chunk>();
	GetNode("Chunks").AddChild(scene);

	GD.Print(scene2.GetType());
	GD.Print("=====");
	// chunk.Initialize(position, chunkSize);
}

For each chunk that I make, I need to call the custom Initialize() method in the Chunk.cs script, but the method is not known.

The output of the GenerateChunk() method is this:

=====
Godot.PackedScene
Godot.Node3D
  /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs:113 - System.InvalidCastException: Unable to cast object of type 'Godot.Node3D' to type 'WorldGeneration.Chunk'.
(mode error stacktrace)...

This means it can instantiate the scene, but only as a Node3D and not a the Chunk. Again, when I run the project it works, but not when it is triggered in the editor.

I don’t understand why it is not working as I have gdscript code with the same setup that does work in the editor. I suspect it has something to do with not everything loaded when it is triggered from the editor when using C# (as that is the only difference I can think of), but how can I make this work in the editor aswell?

I tried loading the Chunk.cs manually, but that didn’t change anything.
Any help is appreciated.

The problem is this:

chunkPrefab = GD.Load<PackedScene>("res://scenes/WorldGeneration/ChunkCs.tscn");

For some reason, Godot does not like how you cast the variable with <PackedScene> for loading scenes. Instead, try this:

chunkPrefab = (PackedScene)GD.Load("res://scenes/WorldGeneration/ChunkCs.tscn");

Please let me know if you run into any more issues. I have been doing terrain gen for a while.

your code and error don’t really show what’s going there…
you needed remove scene from tree before you add again.
obraz

You don’t Needed check what type is node to add to tree. In C# you can use

if(scene is Chunk chunk)
 {
chunk.method();
}

var scene = chunkPrefab.Instantiate(); here your scene is Node you can try cast with previous if statement or

        Chunk scene = chunkPrefab.Instantiate<Chunk>();

but you will have error if in chunkPrefab is wrong scene.

@ThisGameIsRyan
This unfortunately still has the same results. Same output and same error.
I reloaded the project to be sure.

@Moreus
I indeed had GetNode("Chunks").AddChild(scene); by accident, but I already changed that to AddChild(scene2). This however does not fix the problem.

I thought that maybe the scene was the problem. So I created a new scene named ChunkTest.tscn with a Node3D as root and added a new C# script named ChunkTest.cs that extends the Node3D.

Now the the code looks like this:

private void GenerateChunk(Vector3I position)
{
	GD.Print("=====");
	// chunkPrefab = GD.Load<PackedScene>("res://scenes/WorldGeneration/ChunkCs.tscn");
	chunkPrefab = (PackedScene) GD.Load("res://scenes/WorldGeneration/ChunkTest.tscn");
	GD.Print(chunkPrefab.GetType());

	var scene = chunkPrefab.Instantiate();
	GetNode("Chunks").AddChild(scene);

	GD.Print(scene.GetType());

	var scene2 = chunkPrefab.Instantiate<ChunkTest>();
	GetNode("Chunks").AddChild(scene2);
}

But the output is still an error:

/root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs:113 - System.InvalidCastException: Unable to cast object of type 'Godot.Node3D' to type 'ChunkTest'.
     at Godot.PackedScene.Instantiate[T](GenEditState editState) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs:line 20
     at WorldGeneration.WorldGenerator.GenerateChunk(Vector3I position) in [path]\Code\WorldGeneration\WorldGenerator.cs:line 94
=====
Godot.PackedScene
Godot.Node3D

Again, when I run the project (with F5) it works as I can see it work in the remote tab, but running it from the editor gives this error.
Is it even possible to run this code from the editor? It makes testing much easier instead of having to build and run the project for every change.

What error does it give when you do this:

private void GenerateChunk(Vector3I position)
{
    try 
    {
	    GD.Print("=====");
	    // chunkPrefab = GD.Load<PackedScene>("res://scenes/WorldGeneration/ChunkCs.tscn");
	    chunkPrefab = (PackedScene) GD.Load("res://scenes/WorldGeneration/ChunkTest.tscn");
	    GD.Print(chunkPrefab.GetType());

	    var scene = chunkPrefab.Instantiate();
	    GetNode("Chunks").AddChild(scene);

	    GD.Print(scene.GetType());

	    var scene2 = chunkPrefab.Instantiate<ChunkTest>();
	    GetNode("Chunks").AddChild(scene2);
    }
    catch (Exception e)
    {
        GD.PrintErr($"World Generation Error: {e.Message}");
        GD.PrintErr(e.StackTrace);
    }
}
=====
Godot.PackedScene
Godot.Node3D
  Map Generation Error: Unable to cast object of type 'Godot.Node3D' to type 'WorldGeneration.ChunkTest'.
     at Godot.PackedScene.Instantiate[T](GenEditState editState) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs:line 20
     at WorldGeneration.WorldGenerator.GenerateChunk(Vector3I position) in [path]\Code\WorldGeneration\WorldGenerator.cs:line 97

Line 97 would be var scene2 = chunkPrefab.Instantiate<ChunkTest>();

Wait, why are you trying to cast it as a chunk?

Because the idea is to instantiate a new Chunk.tscn scene and then call the method Initialize that’s in the Chunk.cs script.

And that works when I click Run project (F5), but not when running the same code from the editor. As if the chunk script is not loaded when running from the editor.

Try

Node scene2 = chunkPrefab.Instantiate(); 
If( scene2 is ChunkTest)
 GetNode("Chunks").AddChild(scene2);
else 
GD.Print(" scene2 is not ChunkTest");

You could use [tool] in all scripts in use but your errors will not reset, deleting adding nodes will stay. And still needed compile c#

I’m not sure of the way GetType() works with a Godot object but why is it returning Godot.PackedScene from an instantiated instance?

var scene = chunkPrefab.Instantiate();
	GetNode("Chunks").AddChild(scene);

	GD.Print(scene.GetType());

Shouldn’t it be telling you its and instance of a Godot object?

That’s what I would expect. And manually casting it to the expected type results in the error earlier mentioned.

@markieeey

What your result? if you getting message “scene2 is not ChunkTest” that mean your scene2 is a problem.

I get the message scene2 is not ChunkTest.

I changed Node3D to Node to see if that changed anything, but it did not.
I cleaned up my code after all this debugging, but this is my code at the moment (which still triggers the previous mentioned errors).

Chunk.tscn

[gd_scene load_steps=2 format=3 uid="uid://cyc2j83mo5lpi"]

[ext_resource type="Script" path="res://Code/WorldGeneration/Chunk.cs" id="1_00s6l"]

[node name="Chunk" type="Node"]
script = ExtResource("1_00s6l")

[node name="Tiles" type="Node" parent="."]

image

GenerateChunk() method in WorldGenerator.cs

private void GenerateChunk(Vector3I position)
{
	chunkPrefab = (PackedScene) GD.Load("res://scenes/WorldGeneration/Chunk.tscn");
	Node scene = chunkPrefab.Instantiate(); 
	if( scene is Chunk) {
		GetNode("Chunks").AddChild(scene);
		scene.Initialize(position, 16);
	} else {
		GD.Print("scene2 is not Chunk");
	}
}

Chunk.cs

using Godot;

namespace WorldGeneration
{
    public partial class Chunk : Node
    {
        // Called when the node enters the scene tree for the first time.
        public override void _Ready()
    	{
    	}

    	// Called every frame. 'delta' is the elapsed time since the previous frame.
    	public override void _Process(double delta)
    	{
			
    	}

        public void Initialize(Vector3I chunkPosition, int chunkSize)
        {
            GD.Print('This should work, but method can not be found/called.');
        }
    }
}

but you did
scene2 = chunkPrefab.Instantiate();
?

Yes, I get the message scene2 is not ChunkTest.

(post deleted by author)

can you put your code on git?

I guess you were missing the as when instantiating.

var chunkInstance = chunkPrefab.Instantiate() as Chunk;

The “as operator” will return null if the instantiated object is not a chunk.

In the .NET version Godot is using you can simplify the namespace. It’s a bit better to look at without an additional indentation/curly braces.

See below:

using Godot;

namespace WorldGeneration;

public partial class Chunk : Node
{
    public void Initialize(Vector3I chunkPosition, int chunkSize)
    {
        GD.Print($"Pos: {chunkPosition} Size: {chunkSize}");
    }
}
using Godot;

namespace WorldGeneration;

public partial class WorldGenerator : Node
{

	public override void _Ready()
	{
       GenerateChunk(Vector3I.Zero);
	}

    private void GenerateChunk(Vector3I position)
    {
        PackedScene chunkPrefab = GD.Load<PackedScene>("res:///Chunk.tscn");
        var chunkInstance = chunkPrefab.Instantiate() as Chunk;
        GetNode("Chunks").AddChild(chunkInstance);
        chunkInstance.Initialize(Vector3I.Zero, 16);

        GD.Print(chunkPrefab.GetType());
        GD.Print(chunkInstance.GetType());
    }
}

Prints:

Pos: (0, 0, 0) Size: 16
Godot.PackedScene
WorldGeneration.Chunk

@trizZzle This doesw work when running the project (with F5), but not from the editor.

I put my code in github: GitHub - DijkeMark/godot-training
I believe you should be able to access it here.

When you run the project (with F5) you will see it works. But if you select the WorldGenerator node and then in the Inspector under ‘Interactions’ click on ‘Generate World’ you will get the errors.