Some confusion over referencing other Scenes Nodes

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

Hi,

I’d like to clear up some confusion regarding accessing other Nodes in Scenes that are Child Instances. There are many tutorials discussing this topic, but I’ve not found good answer so far that covers most scenarios.

This is something that for beginner’s in Godot which is realy realy confusing - I know I’m not the only one :wink: It’s the biggest hurdle I’ve come accross and the docs are confusing on this too. As soon as you begin to make a game, this quickly becomes a headache.

So far I try to build my Main Level/Scene with Instanced Scenes, e.g.:

(This example below is of a 2D Platfromer)

Scene Tree:

MainLevelScene (Root)
Player
Enemies (Node2D container)
…Enemy1
…Enemy2
…Enemy3
Spikes (Node2D container)
…Spike1
…Spike2
…Spike3
Springs (Node2D container)
…Spring1
…Spring2
…Spring3
etc

From what I’ve learned, as a best practice I should always inform the Player that something has occured by passing information from an Instanced Child to the Player, not the other way around i.e. The Enemy Scene informs the Player he has touched the Enemy, or that the Springs tells the Player he’s stepped on a Spike.

Direct Reference
Directly referencing Instanced with get_node(“…/Player”) is bad practice as this can break the game if the Child Nodes are moved at some point. From what I understand if you have multiple Instanced Children in the Scene, such as Enemy1, Enemy2, Enemy3 etc this won’t work anyway.

Singleton/Autoload
I’ve read that you can Autoload a Scene as a Singleton, but this is typically for the Main Root Node (in this example ‘MainLevelScene’. From what I inderstand, It’s not ideal for individual Scenes.

Signals
This appears to be the best pratice I’ve seen so far, but is quite cumbersome, at least for a beginner. There are built in Signals, such as ones for Area2D nodes where you can check for collisions, entered/exited and you can also create your own.

Having multiple Child Nodes, such as several Spikes in the Main Scene is common, but you need to be able to reference the main Spike’s Scene and not the individual ones in the Scene tree.

So for example I have a Spring that launches the Player sky-high - Boing!!

My Spring’s are made with:
Area2D
…AnimatedSprite
…CollisionShape2D

My Steps:

1) In the Springs script I detect if the Player’s body has entered the Spring and also for exited.

2) I create a Signal that is emitted in the Spring script to inform the Player he has collided with the Area2D.

3) Then in the Player script I can access the emitted Signal and do something with it.

In my case I have a conditional check in the Player’s script:

If The Player is falling and he is overlapping the Spring.

This way he doesn’t accidentally walk into the Spring and get launched upwards. He has to jump onto it first.

But I wish to also play an animation for the Springs bounce - wouldn’t anyone? :wink:

I can do it in the Springs script which is a doddle, but I’ve no idea how to access/reference the Springs animation i.e. $AnimatedSprite.play(“SpringBounce”) from the Player’s script :frowning:

With a Signal I can pass a variable, but what about an animation?

Could someone explain what is the ideal method for this please?

If there are other ways to reference that are better, please also let me know - I’m sure other beginner’s to Godot in my situation would love to know.

Thanks muchly :slight_smile:

:bust_in_silhouette: Reply From: DaddyMonster

There is no silver bullet “right answer” that solves the node communication problem. You only get a better understanding of each approach’s pros and cons as you get more experienced and can assess how scalable and maintainable your project is.

Direct referencing
Yes, this is brittle. If you move / queue free node, it will break. This isn’t actually such a big deal, not exactly hard to debug. You can add a check to see if it returns a node for starters.

Instead of get_node("../Player") you can put:

export (NodePath) var player_path
onready var player = get_node(player_path)

That way if you move it Godot will update the path automatically. Clever, eh?

The real problem is spaghetti code. You want your code to be like a large business a whole made up of departments and divisions. That way if there’s a problem with Accounts you don’t need to check every single department. This is usually called “separation of concerns”. It can become so unmanageable you’ll have to re-start the project.

My rule of thumb (other’s may differ) is to break everything distinct into a scene. Especially if there’s more than one instance. I allow direct referencing within a scene but as rarely as possible between scenes. So, player is a scene. Enemy is a scene. Sure, I’ll use signals when it’s convenient within scenes but I’ll not have an issue direct referencing because I know, within the scene, the issue is contained and easy to debug. Still, I’ll be careful not to allow it to become spaghetti even within a scene. Ask the question: is this code ugly or beautiful?

Singleton/Autoload
Small game jam game. Sure, go for your life.

Big project? You abandoned the separation of concerns. Sometimes there’s an element of your game that spans innumerable components and you can have the choice between a singleton and ridiculous complexity. Don’t do it unless you absolutely have to on a serious project is my advice.

Events Singleton
This is more advanced. Effectively it’s a singleton made of signals. Only appropriate for a biggest game / more advanced dev. I include it for completeness.

Signals
The real advantage of signals is that they allow communication while allowing separation of concern. You can have spaghetti signals too if you go too far so just be sensible. Plus they’re decently expensive. And yeah, they’re a pain to set up sometimes. You want scenes to let each other know about an event? Signals are your go to.

Groups
Add your enemies to a group. Then you can update that group. It’s simple and it works.

Now, other people argue that you should get_node() down the scene tree and signal up. I don’t do buy into this myself, I don’t think it solves the central separation of concerns issue.

So, there’s no right answer. There are only pitfalls, pros and cons. As long as you are aware of them and bear them in mind you can find sensible solutions that fit your game.

Thanks for the educational detail, it’s interesting that you are not as fixed as some Godot users are regarding Direct References, I can see a large game being difficult to manage with them, but on a smaller project used in moderation it could be ok as you say.

Although, one thing that comes to mind as I mentioned already, it’s not possible to use Direct References for multiple nodes that are instanced as children i.e Spike1,2,3…, Enemy1,2,3…, Ladder1,2,3… and so on. A Direct Reference is for a specific node in a tree, correct me if I’m wrong :wink:

Although I’ve been using Signals for most Instanced Scenes. With my Ladder for example I had to get a reference for Ladders Position - so that I could centre the Player on the Ladder in the Player’s script.

But for playing an animation on another Scene’s node, such as a Spring bounce. How do I access the animation in order to play it?

In my Spring script I have an animation:

$AnimatedSprite.play("SpringBounce")

But I’d like to play it from within my Player script. How is this best done?

JayH | 2022-07-28 16:12

It’s often the case that when a coder first learns about concepts like separation of concerns, encapsulation, etc they declare “don’t ever do this!!”

In way that’s fair enough because it’s often very hard to nail down simple rules and, on balance, it might improve code. I advised you to be cautious with singletons for example. But think about it, if they were purely a bad thing then Juan (Godot’s lead dev who definitely knows how to code!) wouldn’t have added them into Godot.

There was a famous paper long ago that made a faultless logical argument that then ubiquitous “GOTO” statement is always bad. No language uses it anymore or ever will again.

If direct references were always bad they wouldn’t be in Godot. They’re a tool with advantages and drawbacks.

Yes, by “direct reference” I mean $Player/AnimationTree, get_node("../../Enemy") or get_parent() where you navigate the tree.

For the ladder’s position. Are there many ladders? Maybe put them in a group and you can grab there position without an issue. If you’re spawning them from your world scene you can always throw them in an array or dict and handle them there. So enemies[0].global_transform.origin.

So, you ask about your animation player.

My rule of thumb is: is the call coming from within the Spring scene? Then direct reference to your heart’s content. If not, use a signal or throw it in a group. That way you know that each scene is self contained.

That’s just a rule of thumb but will product pretty good code. As you get more experienced you can weigh up the pros and cons yourself.

DaddyMonster | 2022-07-28 16:52

Thanks for the info and help again, always interesting to hear other users knowledge and experience.

I don’t have a problem with my Ladders currently, I have them working fine - although I still have to do the moving down from the above platform, but that’s for another time :wink:

I’m trying to create small mini projects with mechanics for all the things I need to know for creating a 2D retro platformer in Godot. That way I can look back at each project and I can combine them into an actual game - keeping it simple! I’ve got most of them done, Springs is possibly my last one, unless I think of some other mechanic later.

Doing it this way makes it easier to read the code and work out how I did things later. Plus I get to fully understand what’s going on without blindly following endless tutorials and getting lost in the mess I may have created.

It’s the Springs that I’m having issue with. If I could explain further…

The Main Level/Scene:

enter image description here

The Main Level/Scene Tree

enter image description here

The Spring Node Tree and Script

A Snippet of the Player Script:

enter image description here

I wish to be able to play the Springs/Mushrooms bounce animation from within the Player’s script. I have one animation for Idle and one for the bounce.

$AnimatedSprite.play("SpringBounce")
$AnimatedSprite.play("SpringIdle")

I could just play the animations from the Spring script, but I need to check some spefic things within the Player’s script in order for the Player to use them.

e.g.

I check if the Player is falling and if he has collided with the Springs.

Direct references wouldn’t work as I will no doubt have more than one Spring in each level/scene. I’m not sure how to use a Signal to play the animations required either. Hopefully this mini project will help me understand references and instances more.

What method is best to trigger the Spring animations from the Player script?

JayH | 2022-07-28 18:51