Here's a mock-up of my script:
# player.gd
extends HBoxContainer
class_name Player
var can_play = false
signal thing
func _ready():
connect("thing", #the following script# , "stuff")
func _input(Input):
if !can_play:
return
emit_signal("thing")
# game.gd
extends Node
var players = [Player0, Player1, Player2, Player3] # of class Player
var current_player = 0
func _ready():
yadda()
func yadda():
players[current_player].can_play = true
func stuff():
players[current_player].can_play = false
print(players[current_player])
current_player = (current_player + 1) % 4
yadda()
My goal here is to have each player get their name printed when it's their turn and on input. Only one player should have their can_play set to true. On every input, the current player has their can_play set to false and the next one set to true.
Now this code works every time except when current_player == 3. If there's an input after that, the code prints both "Player3" and "Player0". Player3 and Player0 have their can_play set to true one after the other with only one input. This doesn't happen for the other Players either.
I have tried setting a Timer so that the code doesn't set can_play to true directly and it was successful. The real problem here is that I don't understand why the code wouldn't work only on Player3 and Player0
This is caused by event propagation. Input events bubble up the tree starting from the deepest leaf nodes.
Main ^
- Player0 |
- Player1 | _input(event)
- Player2 |
- Player3 |
The situation is when Player3
can_play
and the left click input event begins propagating up the scene tree.
Player3
receives the left click input event. It can_play
and signals to Main
to do stuff()
.
stuff()
sets can_play
to false.stuff()
sets current_player
to the next player, Player0
.Player2
receives the left click input event. It can't play.Player1
receives the left click input event. It can't play.Player0
receives the left click input event. It can_play
and signals to Main
to do stuff()
.
Main
receives the left click input event.Call SceneTree.set_input_as_handled()
after handling the input event.
if input.is_action_pressed("left_click") and can_play:
emit_signal("thing")
get_tree().set_input_as_handled()
# Player.gd
func _ready():
connect("thing", get_parent(), "stuff")
The thing
signal connection here should be connected by the parent. For example:
# Main.gd
func _ready():
for player in players:
player.connect("thing", self, "stuff")
This decouples Player.gd
from Main.gd
. Player.gd
no longer needs to know about the method stuff()
.
Generally, signals go up the scene tree and method calls go down the scene tree.