Why might the gate script only work on the first gate in the scene?

Godot Version

Godot 4.3

Question

Hey folks!

I have made a Gate scene, consisting of the following nodes and code. In the level scene, the first gate I drop in works flawlessly. The player types a code when the CharacterBody is within the gate’s Area3D, the signal connects and the gate lowers. But it only works with the first gate I put into the level scene (not the first I interact with or the first in the node tree), and I need several. This doesn’t change if I make subsequent gates local.

I’m sure I’m missing something basic as I’m new here, but I’m not new to googling and yet I haven’t managed to figure this one out, hence me making this profile and humbly asking for your guidance.

extends CSGBox3D

var timer = 0
var opening = 0

func _on_arcane_control_f_1234() -> void:
	
	if $Area3D.overlaps_body(%Player) and opening == 0:
		opening = 1

func _physics_process(delta: float):
	if opening == 1:
		if timer < 400:
			position.y -= 0.1
			timer += 1
		else:
			opening = 2

Can you show the code calling _on_arcane_control_f_1234?

This is the code emitting the signal. It is on a control panel, child to the Player. The signal is sent while 1234 is typed in the buffer, and it works on the first gate in the level. The signal triggers the _on_arcane_control_f_1234 function in the gates.

extends Control

var buffer:=[]
signal f1234

func _input(event):
	if event.is_pressed() and event is InputEventKey:
		buffer.push_back(event)
	
func _physics_process(_delta):
	if buffer.size() > 4:
		var event=buffer.pop_front()
	
	print_buffer(buffer)
	
func print_buffer(b:Array):
	var p:=[]
	for event in b:
		if event is InputEventKey:
			p.push_back(event.as_text_key_label())
	print(p)
	$CenterContainer/Label.text = str(p)
	
	if p == ["1", "2", "3", "4"]:
		#print("open")
		f1234.emit()

I don’t see why you’d want to use _physics_process(), you can achieve the same without it, making it much simpler and much more performant.

extends Control

var buffer:=[]
signal f1234

func _input(event):
	if event.is_pressed() and event is InputEventKey:
		buffer.push_back(event.as_text_key_label())
		if buffer.size() > 4:
			buffer.pop_front()
		print_buffer(buffer)

func print_buffer(b:Array):
	print(b)
	$CenterContainer/Label.text = str(b)
	if b == ["1", "2", "3", "4"]:
		print("open")
		f1234.emit()

I’m not sure if this fixes your described problem though.
I suggest to add some more print statements in the _on_arcane_control_f_1234() method to see what and when is being received.

Right you are! I changed it and it does work just fine without the _physics_process(). I had it in the first place cause that’s what the dude in the tutorial I was referencing was using - https://www.youtube.com/watch?v=x0A0w5-OmlE&.

That didn’t help with the current issue though. What did help was connecting the f1234 signal to every instance of a gate from the signal menu, which I hadn’t realised I need to do. Is there a way to connect the signal once and then have every instance of a gate (or other similar scene) be connected?

Also, you’re right about print statements - I will be using many of them to check functionality for sure.

Unique node names like %Player used here are not Unique in a global sense, the node “Player” would have to be a child of Gate for this to work. Unique names are not for finding one node out of the entire game, they are essentially for shortening very long $node/child/grandchild/greatgrandchild paths within one scene.

Related to the connection I’m sure a fix for this is using the body entered and exited signals to track overlap and the player object at the same time

var player: Player = null

func _on_arcane_control_f_1234() -> void:
	if opening == 0:
		opening = 1

func _on_area_body_entered(body: Node3D) -> void:
	if body is Player:
		player = body
		player.f1234.connect(_on_arcane_control_f_1234)

func _on_area_body_exited(body: Node3D) -> void:
	if body == player:
		player.f1234.disconnect(_on_arcane_control_f_1234)
		player = null

Yes. If you use a signal bus, you can have every instance of the gate generate a signal with the bus, then have your character listen for signals the bus emits.

Something like this, with gate_info being the specifics of the gate that was connected.

SignalBus.gate_connected.emit(gate_info)