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

Created on 6 Sep 2018  路  22Comments  路  Source: godotengine/godot

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.

discussion enhancement core gdscript

Most helpful comment

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.

All 22 comments

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.

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"))

BTW, I'm not really against it, but technically it breaks compatibility.

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.

@vnen I'm glad you're weighing in on it for that reason. These are just the "would be nice" thoughts of an end-user. X)

I am curious though the nature of breakage. I'm not sure if you mean for users going from 3.0 to 3.1, or something more on the internals side of things.

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.

@PLyczkowski At least one way to determine an extended class currently is to do is_class("Node2D") say on a Sprite or something higher up. The base class of most things (if not everything) I believe would just be Object.

@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"

I have also just encountered it and would like if there was at least another method to get the actual class name but that really makes the API dirty. BC break for get_class() would be the cleaner solution.

I think it would make more sense to make the get_class return the class_name...
or create a "Type" structure like C# that return a bunch of packed info regarding the type of a given class.

get_type() -> Type:

then you could have:

type.class -> return the class_name
type.base_class -> returns the base class name
type.path -> returns the path to the gdscript where it is defined
...

Is there any plans for this? This would help tremendously, especially in debugging scenarii. And I guess it's probably not a huge thing to implement.

Here's a clumsy but actually easy/working solution for people using tools capable of mass replacing text across a whole folder/subfolders using regexes, such as IntelliJ Idea.

Replace:

class_name (\w+)\s*\n(?!func get_class_name\(\))

with:

class_name $1
func get_class_name() -> String:
    return '$1'

Do test it on one file though in case your IDE/tool uses different regex syntax (you don't want to fail a mass replace, especially when some editors don't allow that to be undone).

You can run this easily as many times as you want and it'll just add the missing ones.

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.

A very useful use case for this would be in cross-scripting scenarios. Currently, when accessing GDScript scipts from C# code, as far as I can tell, there is no way to check the class name of a node. The workarounds mentioned here do not work, as you cannot overload C#'s IsClass/GetClass from GDScript code.
It is technically doable by using Call(), but that's gonna throw an exception if you try it on anything that doesn't implement the workaround and clashes with the var is class syntax that can be used on C# classes.

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)

You can override the method inside the class:

def class_name():
    return "MyCustomName;"

you need to do this manually though! :(

Sure, but this is rather a workaround than a solution :)
It can be very error prone if you have a lot of classes, since you need to do this manually for each of them.
Cheers

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.

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.

@willnationsdev Perfect

How is is_class() actually written in code, and why does it return false even when I both override get_class() and set class_name accordingly?

@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.

Was this page helpful?
0 / 5 - 0 ratings