Best organized way for an instanced scene to access a property of another instanced scene

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By cardoso
:warning: Old Version Published before Godot 3 was released.

So I have a scene like this:

level
  player (instanced scene)
  enemy (instanced scene)
  item
  ...

Some actions/states of the player depend on the enemies, items, etc, that will be different depending on the level.

The enemy actions are also dependent on the player actions/states.

All player code (and enemy too) is self contained in their correspondent scripts.
Because of this I suppose I cannot use get_node() inside the player to get enemy variables, cause player and enemy are different scenes.

I imagine in the level scene I could just pass variables to the player node (like the enemy position, etc), but is this the best way?

Or would it be better to pass some player logic to the level scene? Cause I also intend to do some manual shape collision checking with the other level nodes/instanced scenes.

:bust_in_silhouette: Reply From: Warlaan

Three options come to mind:

  1. Use signals in the enemy and the player scenes to inform the surrounding scene that they want to influence their surrounding. In your case this might not be the most fitting solution, but I mentioned it first since usually it’s the cleanest solution.
    For example if you want the enemy to always target the player create a “player_moved” signal and let the enemy react to it. Make sure that you don’t use this method if the event is triggered a lot more often than it is reacted to, for example when for some reason the player position might change more than once per frame, otherwise it might be bad for the performance.

  2. Add a setup-function to the classes that expects references to the needed objects. So if your player would need a reference to the enemy you would call something like “myPlayer.setup(myEnemy)”, so the player receives a reference which it can then use time and again to react to its changed properties.

  3. If you don’t need a reference to one specific enemy but you want a reference to something like the closest enemy, the one with the most hitpoints etc. then add a manager class that “knows” every enemy and is able to evaluate them and return the requested one. This last method can also be implemented using existing systems, e.g. by adding a collision object to the player that tracks colliding enemies.

Thanks a lot!
I am already trying your suggestions, to see which of those options fits better in my structure.

cardoso | 2016-06-09 18:30

You forgot about Groups. You can create group for Enemies, and send messages to the group, so enemies can react on player. Or from inside player get all nodes that are in enemies group and check some values etc.

kubecz3k | 2016-06-10 16:24

:bust_in_silhouette: Reply From: pheryx

In my projects I use a generic function to initialize each instanced scene and pass references to the global node, root node, and current node. This gives instanced scenes a reference to all three, which can be traversed upward through parent.parent.function() or downward with root.node_name.node_name.function().

You will always call init_root() before adding the child to the stage. Or, if the scene is instanced inside another scene, you’ll call init_root() from the parent scene’s _ready() function. This way, init_root() is always called before firing any _ready() function. By the time the child’s _ready() fires, it will already have access to your other nodes.

In Godot, every object is passed as reference, so this does not impact your performance. It gives you an easy way to reference your other scenes, nodes, and properties.

The child script:

var global
var root
var parent

func init_root(global_node, root_node, parent_node):
    global = global_node
    root = root_node
    parent = parent_node

func _ready():
    pass

Then, from the parent script:

var internalScene = null
var externalScene = null

func _ready():
    internalScene = self.get_node('instanced_scene')
    internalScene.init_root(global, root, self)
    # or
    externalScene = load('res://scenes/loaded_scene.scn').instance()
    externalScene.init_root(global, root, self)
    self.add_child(externalScene)

Thanks, I am glad I asked about this, cause I am learning a lot.
And adapting your case to my example, what would be the difference between the global and the root nodes?

cardoso | 2016-06-11 10:19

Global is a script you create that can be autoloaded with any running scene. The root node would be the currently running scene. When you change scenes, the new scene will become your root node, but the global script would be loaded the same.

Using a global script is optional. It functions exactly as a node. It allows you to carry common variables, functions, and settings among different root scenes. You can read more in the documentation for Singletons (AutoLoad).

pheryx | 2016-06-11 11:42

Ah ok, you mean global in that sense. I don’t know why but I thought You meant global would be the top most scene holding the other instanced scenes.

cardoso | 2016-06-11 12:38