0 votes

I am trying to make a class which inherits from another class. The below example works except that I can not access anything in the node tree from the inherited class. Please note that subClass is an inner class but also does not seem to work with a normal class (gd script file) be it inherited or not. I can access functions and variables in the class which I inherited from but if I try to call something like "print (get_path())" it just prints nothing and throws an error:

0:00:02:0489 - Condition ' !is_inside_tree() ' is true. returned: NodePath()
----------
Type:Error
Description: 
Time: 0:00:02:0489
C Error: Condition ' !is_inside_tree() ' is true. returned: NodePath()
C Source: scene\main\node.cpp:1780
C Function: Node::get_path

Here is the below code and project file in a downloadable zip.
mainClassScript.gd attached to the root node, a Spatial Node called main:

extends Spatial

var testVar = "this is a test var"

var containsObject

func _ready():
    printPath() # prints: "/root/main"
    containsObject = $sub.subClass.new()

func testFunc():
    testVar = "test var changed!"

func printPath():
    print (get_path())

func _unhandled_input(event):
    containsObject.handleInput(event)

subClassScript.gd attached to a Spatial Node called sub which is under "main" in the node tree:

extends Node

class subClass:
    extends "mainClassScript.gd"

    func _init():  
        pass

    func handleInput(event):

        if event.is_action_pressed('ui_accept'): # press SPACEBAR 
            print(testVar)  # properly prints out: "this is a test var"
            testFunc() # modifies testVar to "test var changed!"
            print(testVar) # properly prints out: "test var changed!"
            printPath() # prints nothing and produces error: Condition ' !is_inside_tree() ' is true. returned: NodePath()

Any help you can offer would be greatly appreciated! Thank you!

in Engine by (121 points)
edited by

2 Answers

0 votes

Docs for get_path():

Returns the absolute path of the current node. This only works if the current node is inside the scene tree (see isinsidetree()).

You can avoid this by doing this:

extends Node2D

func _ready():
    _print_path()

func _print_path():
    if is_inside_tree():
        print(get_path)

The reason the first approach won't work is the order in which things are getting called, I suspect. When you try to call get_path() on your subnode, _ready() was called for itself, but not yet for all of the hirarchy above it.

by (838 points)

Footurist, thank you for your reply! Please correct me if I am wrong but I do not think that this is really the issue. Please also note that the "subClass" is an inner class.

Here is why I think this: I don't actually create the new object withcontainsObject = $sub.subClass.new() until the _ready() function of the main class, after the main class has properly printed a get_path(). I've also tried not creating the new object until much later (with a button press), this also did not work. I think for some reason the objects being created, though an extension of "mainClassScript.gd" just never have a node assigned to them and never will return true for is_inside_tree()

I should mention though that if I put a func _ready(): in my inner class (subClass) like you suggested (I think) it never gets called.

I think the problem is more to do with creating a sub class from a main class and then trying to call the sub class from the main class. Something just isn't working right with that.

Thanks!

0 votes

mainClassScript.gd

extends Spatial

var testVar = "this is a test var"

var containsObject

func _ready():
    readyImpl()

func readyImpl():
    printPath() # prints: "/root/main"
    containsObject = $sub.subClass.new()
    call_deferred("add_child", containsObject)

func testFunc():
    testVar = "test var changed!"

func printPath():
    print (get_path())

func _unhandled_input(event):
    handleInput( event )

func handleInput(event):
    pass

subClassScript.gd

extends Node


class subClass:
    extends "mainClassScript.gd"

    func _init():  
        pass

    func readyImpl(): # override, readyImpl() from mainScript.gd won't be called
        pass # do nothing

    func handleInput(event): 
        if event.is_action_pressed('ui_accept'):
            print(testVar)
            testFunc()
            print(testVar)
            printPath()

You created subClass object but didn't add it to SceneTree.
I've added readyImpl() function that is overriden by derived class. Otherwise subClass object would try to access "sub" child it does not have.

by (2,136 points)
edited by

hilfazer thank you! We are getting close now!

Your suggestion works quite well except you have changed containsObject.handleInput(event) to just handleInput(event). I see why you have done this but it makes every instance of the class and subClasses get called every time. I would really like to be able to call a function that just exists in the subClass, is this possible? Say I want to have two subClasses that both have different versions of the same function, how would I call one or the other? Currently both will be called!

Something like:

class subClass:
    extends "mainClassScript.gd"

    func _init():  
        pass

    func readyImpl(): # override, readyImpl() from mainScript.gd won't be called
        pass # do nothing

    func handleInput(event):
        if event.is_action_pressed('ui_accept'): # press SPACEBAR 
            print("THIS IS CLASS: A") 

class subClassB:
    extends "mainClassScript.gd"

    func _init():  
        pass

    func readyImpl(): # override, readyImpl() from mainScript.gd won't be called
        pass # do nothing

    func handleInput(event):
        if event.is_action_pressed('ui_accept'): # press SPACEBAR 
            print("THIS IS CLASS: B")  

Thanks again!

To remove handleInput() from base class script, You can implement _unhandled_input() like this:

func _unhandled_input(event):
    if containsObject != null:
        containsObject.handleInput( event )

I don't quite understand wha you mean by 2 subClasses. Do you mean adding 2 children to mainClassScript object, one of type subClass, other of type subClassB?

Edit:
You can try disabling input processing for subClass objects by adding this line to their readyImpl()

set_process_unhandled_input(false)

Then _unhandled_input() will be called only in object of base class ("main" node).

I don't quite understand wha you mean by 2 subClasses. Do you mean adding 2 children to mainClassScript object, one of type subClass, other of type subClassB?

Yes this is what I was trying to do. In the end I have given up on trying to extend mainClassScript.gd and just wrote a new class that takes the mainClass as an argument and then calls functions on it that way. IE:

class subClass:
    extends Node
    var mainClass

    func _init(edt):  # this is the constructor
        mainClass = edt

    func doSomething():
        mainClass.testFunc()
        mainClass.testVar = "changed again!" 
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.

Categories