oopinheritancegodotgdscriptgodot4

gdscript local classes: Access class given instance in Godot 4


If I have an instance of a class B which is defined locally, is there any way to access B (as a variable) using that instance?

class B extends A:
   pass

# example
func is_subclass(sub: A, sup: A) -> bool:
    return is_instance_of(sub, sup.get_class_local())
var a := A.new()
var b := B.new()
is_subclass(b, a) # true
is_subclass(a, b) # false

I want to be able to do things like call Object.new() and use is_instance_of(), which I can't do without accessing B. Using get_class() doesn't work, and neither does using get_script() in this case. Is there really no way around this? :(


Solution

  • [I]s there any way to access B (as a variable), using that instance?

    Generally speaking, you can use Object.get_script() to access a class stored as a resource, and use Script functions to probe its properties, functions, and even query its human-readable global name (when registered to ClassDB using class_name).

    Script.get_global_name() is different from Object.get_class(): the latter returns the built-in class name, as opposed to the registered one, as a string. (Why? See here and here.) You can circumvent this behaviour by overriding the get_class() and is_class() functions, as suggested in the PRs above and other places online, or even deploying your own implementation for class instancing at runtime.

    However, since you

    ...want to [...] call Object.new() and use is_instance_of()...

    on object instances directly, we can reformulate the problem differently. If a subclass B is a class that extends a superclass A, then B is of (sub)type A without being strictly A (thus, one of its subtypes). In GDScript:

    func is_subclass(sub, sup) -> bool:
        return (
            is_instance_of(sub, sup.get_script())
            and not sub.get_script() == sup.get_script()
        )
    

    The function above yields the following results:

    class_name A extends Node
    
    class B extends A:
        pass
    
    func _ready() -> void:
        var a := A.new()
        var b := B.new()
        print( is_subclass(b, a) )  # Prints "true"
        print( is_subclass(a, b) )  # Prints "false"
        print( is_subclass(a, a) )  # Prints "false"
        print( is_subclass(b, b) )  # Prints "false"