How do I reference nodes in a tree in a singleton

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By michaelpbl

I have an autoloaded Singleton call ViewManager.
The ViewManager has one Table which has one Dealer which has one Deck and one Judge

I’m building a test frame for my game so it would be useful to access some of these major Nodes directly

I’ve tried a number of ways but I really thought this one would work.
I added these to my Singleton ViewManager (Just showing the code for the Deck node)

var Deck

func registerDeck(NodeType):
Deck = NodeType
print("registerDeck: " + str(Deck))

func getDeck():
print("Getting Deck " + str(Deck))
return Deck

References in scripts of other nodes to ViewManager.Deck or ViewManager.getDeck()
return a null. I have ensured the Deck node has been instantiated by including the registerDeck function which is called in the Deck _ready() script


The register functions work as the following printout shows:

registerDeck: [Node2D:2183]
registerJudge: [Node:2184]
registerDealer: [Node2D:2182]
registerTable: [Node2D:2161]

Yet, when I try to utilize the reference (in the Judge’s testing method in this case) after the reports above (along with several intervening reports) have been made


I get

Getting Deck [Object:null]

The first report comes from the print statement in the ViewManager getDeck function
the second from the print statement in the Judge testing function
The Deck reference, which had been set, is now null.

This is baffling, since Deck is a variable that gets set once and then is only read. I had thought originally it might be due to threading but too much time intervenes between the reports for that to be likely.

Are you sure the Deck node has been instantiated (and, therefore called the registration methods) prior to the Judge node requesting the registered information?

I guess you could tell by the order of the print statements. For example, do you see this:

Getting Deck [Object:null]

Before you see these?

registerDeck: [Node2D:2183]

jgodfrey | 2020-11-02 21:23

Yes, I’m sure. The RegisterDeck and other print statements occur first.
The Getting Deck statement happens afterwards - with a number of prints in between. The Deck and others operate for the entire duration of the game

michaelpbl | 2020-11-02 21:31

If this is still a problem, I’m happy to look at the code if the project is available somewhere?

jgodfrey | 2020-11-04 17:40

Thank you very much. I now have a workaround but I’m not happy with it and would like to do better. I created a skeletal version of the project to make it easier to see what’s going on. In particular the skeletal version needs no server so can run standalone. Unhappily, even zipped, it’s about 143 Mb. I tried to use gitHub but it’s complaining as I have over 100 file in my sprites folder.
So I am stuck for how to get it to you.
I could put it in my dropbox account and provide you with a link

michaelpbl | 2020-11-06 17:28

Yeah, that’s fine if you want. Though, it would seem relatively easy to set up a single, simple, standalone project that would either 1) replicate the problem very cleanly, or 2) would work (and might shine a light on what’s wrong in your main project).

jgodfrey | 2020-11-06 18:07

Thank you for your thoughtful advice. I did create a stripped down version and I did learn a lot from it. Then I went back and refactored my original code. It’s taken a lot of work which is why I’ve been so long answering.

As an old Java and C++ programmer (although it’s been almost ten years since I wrote any code at all), I’m very used to using call backs. I’m also used to designing systems from the top down, often using UML. However I had to learn Godot from scratch, originally from online tutorials which focus on which buttons to push, later from hundreds of online searches. Consequently, I started small and built out, i.e. bottom up.

My project has a ViewManager which has a table which has lots of buttons as well as seats and a dealer. The seats each have a player who has… Similarly the dealer has a deck which has cards. A classic tree. The manager would call down through the chain, until the call reached the right script. Many of the scripts were executing callbacks up the chain.

This created circular dependencies which, unlike C++ and Java, Godot does not handle well. I exacerbated the problem from time to time by using class_name and then applying typing to variables. For example

func getCard()->Card:

where Card is a class_name. This has the advantage of getting the use of Card checked at compile time. It has the disadvantage of introducing more dependencies. Furthermore, it should not be used for entire scenes with multiple nodes.

So I refactored, eliminating class_name, replacing many of the callbacks with signals, and splitting my manager into two. It is autoloaded and was providing services as well as managing. Now I have a separate Global Service script which is also autoloaded (first) which handles service requests and contains system wide data. It knows nothing at all about any script which calls upon its services. It has also eliminated the need for the kind of registration I was asking about in the first place.

The result has been a much improved system. Thank you for your help.

michaelpbl | 2020-11-12 13:42