Why are my duplicate nodes seem to be sharing variables?

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

I have created two nodes. One is a chest with sprite and open animation. The other a door with sprite and open animation.

By themselves, they work perfectly fine. But when I duplicate them, they seem to be sharing the same bool variable for opened. The symptom is that if i open one door, then go to the second door and open it, the previous door will close and vis versa. If the variables are set to private and I duplicate a node, the node name changes and creates a new node right? So the parent name for that node should be say, door2, by default and they should not be sharing a variable.

I am triggering the opendoor function with an emit_signal(“open”) from another script. Could this code be the issue?

Very short code is post here. This is for the door, the chest is the same just renamed to key words to chest.

extends StaticBody2D

const type: String = “door”
const defaultframe: int = 0
var _opened: bool = false

func _ready():
$Sprite.frame = defaultframe

func opendoor():
if _opened == false:
_opened = true
$doorplayer.play(“dooropen”)
elif _opened == true:
_opened = false
$doorplayer.play(“closedoor”)
return

:bust_in_silhouette: Reply From: Bernard Cloutier

If the variables are set to private

There is no “private” in gdscript, just like in python.

they seem to be sharing the same bool variable for opened.

Try placing breakpoints on the lines where the _opened variables are changed. You’ll quickly figure out if it’s the same variable or not.

Since I cannot see the rest of the code (especially the code for the chest and the code that handles the signal emission), I cannot really help you more than that. However, I have a feeling that you might have connected the “open” signal to both the door opening and the chest opening functions. Is that the case? When you emit a signal, every node that is connected to it will receive it.

[Formatting tip: to share code, highlight the lines of code and press “ctrl+k” or click the two brackets {} icon above the text box.]

Thank you for your response. I’m very new to Godot so I’ve got some learning to do. I was interpreting the documentation wrong for variables. I thought that adding a underscore before the name made it private but its only a naming convention. I inserted breakpoints as you suggested and they indeed share the same variable as they are using the same script. I was under the impression that when i duplicate a node, it duplicates the script with it and attaches to the new node with the new name making it a separate script. If this is true then I’m still confused. Am I not duplicating them correctly? Below is the full code for the action.

The function interact() is what I am using to detect when an input key is pressed “E” from controls script as “use” then triggers the rest of the functions based on the type and name of the overlapping bodies parent node name with colliding 2D rectangle boxes. Code flow in post is top to bottom.

func interact():
	if controls.use():
		for area in $Area2D.get_overlapping_areas():
			if area.get_parent() != null: 
				use_signal(area.get_parent())

func use_signal(node):
	#print(node)
	match node.get("type"):
		"chest":
			connect("open", node, "openchest")
			emit_signal("open")
		"door":
			connect("open", node, "opendoor")
			emit_signal("open")

extends StaticBody2D

const type: String = "chest"
const defaultframe: int = 3
var opened: bool = false


func _ready():
	$Sprite.frame = defaultframe

func openchest():
	disconnect("open", self, "openchest")
	if opened == false:
		opened = true
		$chestplayer.play("chestopen")
	elif opened == true:
		opened = false
		$chestplayer.play("closechest")
	return

extends StaticBody2D

const type: String = "door"
const defaultframe: int = 0
var opened: bool = false

func _ready():
	$Sprite.frame = defaultframe

func opendoor():
	if opened == false:
		opened = true
		$doorplayer.play("dooropen")
	elif opened == true:
		opened = false
		$doorplayer.play("closedoor")
	return
    	

dtba | 2020-02-19 23:36

I reviewed my code and I added this line to the bottom of the use_signal() function and it fixed the issue. I was fooled by the scripting and instancing. I breakpoint and checked the bool and it was indeed set to true by either chest or door I opened and or closed. I hope what I am saying makes sense and I am understanding the information correctly. Thanks again for your suggestion. I am still learning the syntax for GDscript.

disconnect(“open”, node, str(“open”,node.get(“type”)))

code posted here and seems to work.

func use_signal(node):
	match node.get("type"):
		"chest":
			connect("open", node, "openchest")
			emit_signal("open")
		"door":
			connect("open", node, "opendoor")
			emit_signal("open")
	disconnect("open", node, str("open",node.get("type")))

dtba | 2020-02-20 00:00

Glad you could find a way that works for you. I can give you some advice on how to use signals though, since the issue seems that emitting the “open” signal was triggering both “opendoor” and “openchest”, since “opendoor” wasn’t disconnecting itself like “openchest” did. [Edit: actually, the line disconnect("open", self, "openchest") in openchest doesn’t do anything, since you are trying to disconnect from the “open” signal, but the “open” signal isn’t on the chest node, it’s on the player node.]

Usually, you do not connect to a signal, immediately emit it, and then disconnect it. That is just a complicated way of calling a function. Signals are used to notify connected nodes, allowing them to react to it. Connecting a signal to a function usually happens in the _ready function, or any other sort of setup or initialization logic.

In your use_signal method, there is no benefit to using signals instead of just writing node.opendoor(), or simply node.open(), with the open method having a different implementation in the “chest” script and the “door” script, removing the need to match on the “type”.

So you want to notify the nodes close to your player that the “interact” action was used. Here’s a way that would leverage signals in a useful way (I’m not saying you should do this, just giving you a possible approach. I like nerding out on design patterns). First, you could put the interactable objects on a layer named “Interactable”, and set the collision mask to collide with your player layer (see this tutorial if you’re confused: Your first game — Godot Engine (3.2) documentation in English).

Then, you connect your interactable objects’s Area2D body_entered and body_exited signals to new methods on their scripts (in your case the “chest” and “door” scripts). These methods only serve to connect and disconnect signals.

ex:
In the player script:

signal interact # or open, activate, whatever suits your game
                # this is what the interactable objects will connect to

func interact():
    if controls.use():
        emit_signal("interact") # does nothing if no one is connected

In the chest script:

# since the area only collides with the Player layer, 
# I named the parameter "player". Learning to use
# collision layers and masks is useful and avoids 
# having something like:
#     if node.name == "Player":
#         ... do the thing with player
func _on_Area2D_body_entered(player):
    player.connect("interact", self, "openchest")

func _on_Area2D_body_exited(player):
    player.disconnect("interact", self, "openchest")

func openchest():
    if opened == false:
        opened = true
        $chestplayer.play("chestopen")
    elif opened == true:
        opened = false
        $chestplayer.play("closechest")

Same thing for the door script.

Do you see the benefit? The player does not even have to know the nodes it is interacting with. He is just exposing the possible actions via signals and emits those signals when the action is triggered. It is up to the surrounding nodes to connect to those signals if they want to react to it. This removes the need to know the type of the node, whether it’s a chest or a door or a lever or an npc etc. Right now you only have chests and doors and only one signal, but can you imagine how annoying and error prone the use_signal would become as you add different objects and different signals? This approach makes it easier IMO.

Anyway, good luck on your game!

Bernard Cloutier | 2020-02-20 18:07

Thank you. Face palm on myself. Lol I chose emit_signal to open the door or chest because that was the solution my mind went with. Im pleased I was able to get it working but as you said, i already have the node name available to me so just call the function. Either way i learned something about signals and plan on trying your suggestion about layering and signaling. That way i dont have to sort types at all. Thank you again.
I really enjoy this software so far. Once learning it, stuff is so easy to work with.

P.S. Im replying on my phone. The service medium cable for my internet is damaged and my internet is down til it is fixed. So if my formatting is crappy or incorrect, that may be why.

dtba | 2020-02-21 00:58

I tried your suggestion and it works. Code is shortened as well. Able to emit_signal if E button is used and then connect to each node based on the body entered connection and open the door and or chest. Then disconnect on exit. Took me a minute to figure out i had to connect the Area2D of the chest to the node chest in the tree via the body entered and exit signals through the node panel. But i got it working. Thanks again. I learned something.

dtba | 2020-02-21 02:16