godot: get_class() and is_class() not returning class_name

v3.1.alpha.custom_build.5307043

The new class_name keyword doesn’t affect the results of the methods for get_class() and is_class().

I noticed this earlier when encountering https://github.com/godotengine/godot/issues/21461, and it feels like maybe it might have been overlooked. Not sure if it falls under bug or a feature request, or if maybe its even intentional and required.

It would be very useful to have these methods return the custom class types in certain situations.

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 110
  • Comments: 51 (16 by maintainers)

Commits related to this issue

Most upvoted comments

Yeah, with 4.0 breaking compatibility, it’s a good opportunity to make this much cleaner:

# my_node.gd
extends Node
class_name MyNode

# derived_node.gd
extends MyNode
class_name DerivedNode

# usage
my_node.get_class() # "MyNode"
my_node.get_base_class() # "Node"
my_node.get_native_class() # "Node"

derived_node.get_class() # "DerivedNode"
derived_node.get_base_class() # "MyNode"
derived_node.get_native_class() # "Node"

This provides you with full access to everything you need. Just need to have the get_base_class() method check for a script first, check if there’s a base script, check if that script is a script class, i.e. a global class, and then return that name if all true, rather than just the native class. And if there is no script, then you revert back to using ClassDB.get_parent_class().

Of course, to make this work well, we also need to have script class support for all programming languages. I’ve got a branch with VisualScript support, but my C# one has been broken for a long time cause I haven’t been able to get the ScriptClass class attribute I made actually detected by the mono runtime.

In my opinion it seems really odd to me that it doesn’t return the custom types. It should definitely be documented though either way.

@avencherus This thread is about this new functionality: https://godot.readthedocs.io/en/latest/getting_started/step_by_step/scripting_continued.html#register-scripts-as-classes

So for example this would work like this:

extends "res://effect.gd" # extends Node, class_name Effect
class_name Damage

func _ready():
    self.get_base_class() # Returns Node
    self.get_class_name() # Returns "Damage"
    var effect = load("res://effect.gd").new()
    effect.get_class_name() # Returns "Effect"

On my opinion, first, I agree with the suggestions here and second I want to add my experience:

I had the get_class() function shadowed, on Godot 3.x. Now, trying to go to Godot 4 I decided to de-shadow all. And for my surprise, all things broke because I depend too much on get_class() on my code just to determine what kind of Item is it. And everything returns “Node2D”!! LoL. And the player character too returns “KinematicBody2D”, so my player is of class “KinematicBody2D”. xD I don’t know from what realm is that character but I think he is from the town of the computer science xD

Well, now I have to use something like “item is Weapon”. Leaving me sometimes lead to recursive references that ends in an error on the Godot Editor. So that is not always possible.

I wanted to share my experience to make it more important to take in account. Because I really depends on knowing the class_name. And as I read above I am not the only one. I don’t know for what Godot use get_class() internally but I think it is important for the right functioning of the engine. At least can we have some other function to return a String of the class_name of our own scripts? I hope yes.

Now for my characters I decided to create a redundant function just to define the same as class_name but returning a String. And for items I need to save that to a file so I really don’t know how I will do for now. Because I need to save the class_name to a file so I can load the same item class. LoL, that really broke my game!

I would maybe go with get_class() -> returns base class name, and get_class_name() -> returns class_name? If that’s too confusing get_class() could be renamed to get_base_class(), and get_class() would then return class_name.

It doesn’t look like you can override get_class() in godot 4.0. I’ve attached an example project, just run main.tscn

class_name CustomClass
extends Node3D

func get_class() -> String:
	return "CustomClass"

func _ready() -> void:
	print(get_class())

godot-override-get-class.zip

Overriding (shadowing) core functions in Godot4 are not allowed, I had to learn that painfully. It works only when you call the shadowed function over the custom type, and will be never work when the engine itself is calling. You can test it by

var node : CustomClass = CustomClass.new()
prints(node.get_class()) # prints "CustomClass"

# assign to the base class
var node : Node3D = CustomClass.new()
prints(node.get_class()) # prints "Node3D"

I don’t think changing get_class() behavior is a good idea. Yes, we should unify the three-level type system we have, but in a different way. For example, with a class that represents a type, see the discussion:

  • godotengine/godot-proposals#6502

I have a GDType class prototype (written in GDScript). I hope to finish it soon and post it to test the idea.

Screenshot from 4.1

Screenshot with #79366

This is a great time to change behavior, the more versions change, the heavier the compatibility baggage will be, and I hope this issue will be resolved soon!

Also, it’s not clear what would this return if the script class don’t have a name.

I imagined that the class_name would override the native class name if you used it. If you didn’t it would work as usual. At least in my mind extends Sprite would report being a “Sprite”, but if you tagged on class_name CustomSprite, it would start reporting that it was a CustomSprite.

It never worked with inner classes, which also have names.

Good point. First time looking at those in 3.1, it seems at least class_name isn’t valid there, but at least not needed for is to compare with them. So then it would only exclude them from being pushed up into the node list.

Since it would be enhancement request, and this makes sense to do, maybe inner classes would override in the same way with their given names.

The usefulness for having class_name appear over top the predecessor would be for some niche situations here and there, such as match and a few other things. Nothing major, but seems like it would be expected to work this way.

match node.get_class():
	"Polygon2D": print("Poly")
	"Sprite": print("Sprite.")
	"AnimatedSprite": print("AnimSprite")
	"CustomSprite": print("My sprite.")

In the meanwhile I’m doing an override in every custom class to enable that behavior:

extends Node2D

class_name CustomClass

func get_class(): return "CustomClass"
func is_class(name): return name == "CustomClass" or .is_class(name) 


func _ready():
	print(get_class())
	print(is_class("CustomClass"))
	print(is_class("Node2D"))

Would it be possible to create a method called get_script_class and is_script_class and leave anything else as is?

class_name doesn’t change what class the script is so it also should not change what is returned by get_class(). If i had get_class() that was returning “bar” previously but now is returning “baz” i’d think that the class has changed. But it didn’t - the script just started using class_name in order to make itself globally accessible or to be registered in the editor.

Which brings us to another problem - class_name already does too much: https://github.com/godotengine/godot-proposals/issues/1047. Adding even more functionality to it sounds like a bad idea.

And before someone misunderstands - i support the idea of giving access to the information OP is asking for. I support it so much i want it available to all our classes (files), not just those with class_name. Therefore i don’t want this information being held hostage by the class_name.

Recently happened upon this when wanting to create a list from a custom class type “StatusEffect” with child scripts such as “BurnStatus” and “PoisonStatus”.

It now requires that I set up some sort of enum or resource_name to compare the different children or need to do a long list of If statements to manually check: If status is BurnStatus and other_status is BurnStatus.

overriding get_class() now also pushes an error, so that is not an option.

having access to the custom class_name when calling get_class() would be extremely appreciated behaviour and is also what one would expect it to do at first glance.

Since Godot 4.0 is supposed to have a number of BC breaks anyway, making this behave as expected instead of the way it does now would be ideal.

It doesn’t look like you can override get_class() in godot 4.0. I’ve attached an example project, just run main.tscn

class_name CustomClass
extends Node3D

func get_class() -> String:
	return "CustomClass"

func _ready() -> void:
	print(get_class())

godot-override-get-class.zip

This is frequently the desired behavior in low-level engine code

Though that would not be in GDScript, correct? I can see how this came about by simply exposing the underlying engine method, but surely for use in GDScript the behaviour requested here would make more sense?

Someone mentioned in #62273 that Script::get_global_name() needs to be exposed to gdscript and that it’d resolve this problem. What exactly does Script::get_global_name() do? Is it just a cleaner, standardized way to get the classname when a custom class is present?

Recently happened upon this when wanting to create a list from a custom class type “StatusEffect” with child scripts such as “BurnStatus” and “PoisonStatus”.

It now requires that I set up some sort of enum or resource_name to compare the different children or need to do a long list of If statements to manually check: If status is BurnStatus and other_status is BurnStatus.

overriding get_class() now also pushes an error, so that is not an option.

having access to the custom class_name when calling get_class() would be extremely appreciated behaviour and is also what one would expect it to do at first glance.

For the time being I add get_class_name() as a custom function down the inheritance tree returning a string that I put in manually. But definitely looking forward to 4.1.

The 3.5 class_name behavior just gave me a headache in a project I’m working on.

I have a set of “SpawnSpot” nodes and a “SummonBrain” node for a game that spawns enemies on a timer; the SummonBrain tracks the timer and some other persistent info (what level enemies should be, etc) and the SpawnSpots handle the logic for deciding where to place an enemy (checking that the target location is empty, etc). Initially, I had SpawnSpots look for the SummonBrain by absolute path, but I wanted to make this more robust–I don’t like the idea of having to place the SummonBrain in the right place in the scene tree, with the right name, every time. It would be better if I could find it by classname, to avoid future problems if I want to rename the SummonBrain or create a new type extending from it.

So I grabbed a snippet for finding a node by class…but it didn’t work. It reported a Node2D instead of the class_name I had set. What gives? I can create a node of class SummonBrain in the editor, right? In fact, that’s exactly what I did to add the SummonBrain to my testing scene! Surely it’s a node of class SummonBrain, since the editor says it’s a node of class SummonBrain: image

The expected behavior is that when I create a node of class X, and the editor UI says it’s of class X, because I have class_name X in the script that defines it, X.get_class() returns “X”. A node of class X is a node of class X. I don’t see why you’d expect otherwise–I don’t expect float.get_type() to return anything other than “float”. I don’t expect Node2D.get_class() to return “Node”. This behavior feels like a holdover from before class_name was added to GDScript.

Edit: The really weird thing is that the is keyword has been behaving as expected. Breaking backwards compatibility was raised as a concern early in this thread, but it seems that the expected behavior already exists–just not for every part of the API that handles class names.

What about adding get_extended_class() is_extended_class() (or similar) which can work on class_name as well as any class in the extension chain.

the code editor will also freak if you try to pass any built in type as an static Reference argument, which is certainly a bug. during runtime itll work no problem but the compiler will only let you pass a built in type into a static GDScriptNativeClass argument

i really hope that these issues are fixed, because working with custom types is super difficult, and its been plaguing the engine for quite some time, which is odd being that godot is object oriented

get_class override works but comparing strings aint got nothin on comparing actual types

@mechPenSketch internal engine methods never call script methods (except for virtual methods which are intended to be overridden). So the script get_class() won’t be called even if the engine uses it internally.

I was about to open a new issued but stumbled upon this one instead. I confirm that it’s a bit confusing to have the native class returned. I had 2 classes : with class_name = “Collectible” OR “Damageable”, Both are Area2D. I needed to get which class they are at some point, but get_class() returned Area2D for both. Which led me to get rid of class_name all together, and add variable ‘name’ instead. I think the clearer approach would be to have get_class return class_name. But if it’s not, it should return the parent class (Area2D in this specific case)

I have an object that, when it enters the scene, it needs to connect relevant signals. I’d like to use class names in this process. To accomplish this I’ve overloaded get_class(). I expect I’ll need to this soon too:

func is_class(name:String)->bool:
   return .is_class(name) or (get_class() == name)

This feels extremely clumsy. This functionality could be all contained with the class_name HelloWorld syntax but instead I have to add a overload of get_class() and is_class() to every class. This is 5 lines for what really should be 1.

This is expected, those methods get the name of the native class. It never worked with inner classes, which also have names.

Also, it’s not clear what would this return if the script class don’t have a name.