Additional derived classes in GDExtension not receiving engine method calls (_notification, _physics_process, etc.)

Godot Version

4.2.1, 4.3.dev5


I’m not new to Godot, but I am new to experimenting with GDExtension.

I followed the GDExtension example documentation and have a custom GDExtension I called Lando derived from Node3D performing the way I expect. Nice!

Then I derived another class I called PlayerBody from CharacterBody3D. I register the class in register_types the same way as Lando.

PlayerBody::_bind_methods() is definitely getting called but when Lando adds a PlayerBody node using add_child, PlayerBody is added to my scene, but PlayerBody::_physics_process is not being called.

ClassDB::class_exists(“PlayerBody”) returns true so my class exists, but ClassDB::class_has_method(“PlayerBody”, “_physics_process”)) returns false.

Adding BIND_VIRTUAL_METHOD(PlayerBody, _physics_process); doesn’t help, but I do see a runtime error as a result:

E 0:00:00:0668 bind_virtual_method: Virtual ‘PlayerBody::_physics_process()’ method already registered.
<C++ Error> Condition “type.virtual_methods.find(p_method) != type.virtual_methods.end()” is true.
<C++ Source> godot-cpp\src\core\class_db.cpp:336 @ bind_virtual_method()

I’m not sure if there’s some detail I’ve missed, or if I’ve found a bug… Doing some searching I feel like it could be related to some bugs related to virtual methods mentioned here: How to bind a c++ virtual method to gdscript - #4 by coder

Any suggestions are appreciated. Thanks for reading!

I’m less inclined to blame a bug because I just watched a YouTube video where it seems like the creator is doing exactly what I am trying to accomplish.

The problem I am having is that Godot is treating any class I create other than the class that is the GDExtension entry-point as the parent class instead of my derived class.

I notice if I use the Print utility function on the this pointer there is a difference:

Lando - Node3D:[Wrapped:0]

PlayerBody - <CharacterBody3D#27397194995>

This is the first I’ve seen of the Wrapped class. I suspect there’s some mechanism that is wrapping my GDExtension entrypoint class that is not happening for PlayerBody, but I’m not clear on what it is…

Any ideas?

Thanks to Naros in the GDExtension Discord for helping me find the answer.

The reason Godot was not aware of my derived classes was because you need to create them using memnew (or memnew_arr if you’re making a bunch of them) in order for Godot to put a Wrapper class around them.

I just had my PlayerBody class as a class member of Lando, so it was being constructed implicitly. Changing the class member to a pointer and constructing it using memnew has the expected effects.