I also have a function that creates an interface whenever I am clicking on a building:
func _on_building_static_input_event(viewport, event, shape_idx):
click += 1
if event.is_pressed() and click > 1:
var func_button = interfacePanel.get_child(4)
func_button.set_text("Cut" + ' ' + str(houseid))
func_button.visible = true
func_button.pressed.connect(self.CutArea)
So I am creating an interface button (it works okay) and connecting it to a function that makes a simple print:
func CutArea():
print('Test works from building ', houseid)
This function indeed prints a text, but if I have 2 or more buildings, all the IDs of those buildings and same amount of line (like if I have 2 buildings > I have 2 lines of output) output.
I clearly can see that interface is different, here are examples:
So I expected that if I will click āCut 3ā I will have an output of āTest works from building 3ā but instead I am getting this:
I assume that something is wrong with way of connection button to a script, but canāt figure out the correct way. Godot 4.2!
With the information youāve provided, this is pretty hard to explain. Sharing the full script (or even entire project) might help here. That being said, your code for _on_building_static_input_event strikes me as a bit odd: What is the initial value of click? And what is that second condition click > 1 for? Also, what is happening with the previous func_button when you click again?
Anyway, the behavior youāre seeing should only occur if clicking on āCut 3ā somehow also is registered as a click on āCut 20ā, but I donāt see any reason it would.
Basically, each click overwrites the pre-created interface panel values. Here is the full code of the script:
extends Node2D
# Export variables
@export var resourceToProduce : ResourceType
@export var buildingName : String
@export var jobType : Job_Type
# Managers
@onready var resourceManager
@onready var peasantManager
@onready var workPlacementManager
@onready var interfacePanel
@onready var peasantSlots
@onready var tilemap = $"../TileMap"
@onready var spot1 = $Spot1
@onready var spot2 = $Spot2
# Debug variables
var click = 0
# Local variables
var houseid
var maxCapacity = 2
var currentCapacity = 0
var currentWorkers = []
# Area variables
var isDrawing = false
# Called when the node enters the scene tree for the first time.
func _ready():
# Setting values.
houseid = randi() % 50
resourceManager = get_node("../ResourceManager")
peasantManager = get_node("../PeasantManager")
interfacePanel = get_node("../TileMap/CanvasLayer/BuildingPanel")
workPlacementManager = get_node("../WorkPlacementManager")
peasantSlots = ["../TileMap/CanvasLayer/BuildingPanel/peasant 1", "../TileMap/CanvasLayer/BuildingPanel/peasant 2"]
$Timer.start()
workPlacementManager.add_work_placement(self, jobType)
func _on_building_static_input_event(viewport, event, shape_idx):
click += 1
if event.is_pressed() and click > 1:
for i in peasantSlots.size():
get_node(peasantSlots[i]).visible = false
interfacePanel.visible = true
interfacePanel.get_child(0).set_text(buildingName + ' ' + str(houseid))
interfacePanel.get_child(1).set_text(resourceToProduce.name)
interfacePanel.get_child(2).set_text(str(currentCapacity) + "/" + str(maxCapacity))
interfacePanel.get_child(3).get_child(0).texture = $Sprite2D.texture
interfacePanel.get_child(4).visible = false
var func_button = interfacePanel.get_child(4)
func_button.set_text("Cut" + ' ' + str(houseid))
func_button.visible = true
func_button.pressed.connect(self.CutArea)
if currentWorkers.size() > 0:
for i in currentWorkers.size():
get_node(peasantSlots[i]).set_text(currentWorkers[i].firstname + " " + currentWorkers[i].lastname + " (" + str(currentWorkers[i].energy) + "/100" + ")")
get_node(peasantSlots[i]).visible = true
elif currentWorkers.size() > 0 and currentWorkers.size() < maxCapacity:
var counter = 0
for i in currentWorkers.size():
get_node(peasantSlots[i]).set_text(currentWorkers[i].firstname + " " + currentWorkers[i].lastname + " (" + str(currentWorkers[i].energy) + "/100" + ")")
get_node(peasantSlots[i]).visible = true
counter += 1
for x in peasantSlots.size():
x = counter
get_node(peasantSlots[x]).set_text("")
counter += 1
else:
for i in peasantSlots.size():
get_node(peasantSlots[i]).set_text("")
func _on_timer_timeout():
resourceManager.updateResourceAmount(resourceToProduce)
func assignWorker(worker):
currentWorkers.append(worker)
currentCapacity += 1
if currentCapacity == 1:
moveWorker(worker, spot1.global_position)
else:
moveWorker(worker, spot2.global_position)
func removeWorker(worker):
currentWorkers.erase(worker)
currentCapacity -= 1
moveWorker(worker, worker.SpawnPoint.global_position)
func moveWorker(worker, toPos):
if tilemap.is_point_walkable(toPos):
worker.current_path = tilemap.astar.get_id_path(
tilemap.local_to_map(worker.global_position),
tilemap.local_to_map(toPos)
).slice(1)
func CutArea():
print(houseid)
print('Test works from building ', houseid)
āclickā condition just there for testing purposes (iām currently instantiating buildings on left click, and also interface is being opened on left click, so there was a bug when I was just instantiating a building an interface was immediately opened, this temporary fix allows me to not open the interface immediately after instantiating).
So basically yeah, I have some placeholders created in my canvas:
And Iām overwriting them each time I click on building. I thought that it will overwrite the funcition as well in a sense.
extends Node2D
var click = 0
@onready var button := get_node("Button")
@onready var id := randi() % 50
func _on_area_2d_input_event(viewport: Node, event: InputEvent, shape_idx: int) -> void:
click += 1
if event.is_pressed() and click > 1:
var func_button = button
func_button.set_text("Cut" + ' ' + str(id))
func_button.visible = true
func_button.pressed.connect(self.CutArea)
func CutArea():
print(id)
So clicking the Area2D (or whatever else youāre using that sends an input_event signal, Iāll assume that part works properly) will show a button, and clicking it will call CutArea(), which prints the id.
But if you instantiate that same scene multiple times, clicking a button will not only print the id associated with it, but also the id of the other buttons? Because if so, I canāt reproduce that with the code above!
Basically this is an area2d, yes. As far as I understand, this function handles all events that are happening in this area (it could be click or just mouseover, for example). But yeah, this part is working as expected anyway.
I have one single instance of the interface panel (structure is in the my previous comment) with this code I am overwriting values in the interface panel with the data from the object I clicked on.
And yes, it prints the IDs of all the buttons, itās correct, and I need to print only the ID of the object where the button was clicked.
UPD.: As far as I understand all the buttons are being connected to the script, and it doesnāt matter which button triggers the script, all connected buttons will be triggered as well.
Ah, got it! Youāre re-using the same button, interfacePanel.get_child(4), in each building script. So when a building is clicked, you only change the text of the button, but the actual instance remains intact and all connected signals as well.
Yes, I think your description is correct. This is the problem.
And, letās imagine I have two instances of the same building (House ID1, house ID2) and if clicking the button I want to print output āID1ā or āID2ā, not both.
I was trying to pass values to the function with .bind option, but it didnāt work. So I was thinking if it is possible to trigger the script and pass the attribute to the script without any .connect or .bind, but didnāt find a solution yet.
Well, your problem is not that the right function isnāt called. And your problem is not that the function isnāt printing the right thing, either. Your problem is that thereās more than one function called on each click! Both buildings have connected their CutArea function to the pressed signal of the same button. Using bind wonāt change that.
Fixing this isnāt trivial, though. You could disconnect the old signal, but only if you knew the callback it was connected to previously ā which you donāt. Or you would actually create an entirely new instance of the button, but you might have reasons not to. Or you could pass CONNECT_ONE_SHOT = 8 as a flag to connect, but then clicking on the same button repeatedly wouldnāt work, obviously.
Yep, I was thinking about creating a new button. The problem here is that I have to store array of those temporary buttons somewhere else, since Iām not sure how to interconnect everything. But yeah, it seems to me now that this is the only option.
Idea was to create a button, save it in array, then, upon next click, check the array, somehow delete the button from array and create a new one instead.
Yeah, itās very reasonable, but in my ideas different building might have different amount of buttons, so I want to find a better approach for solving this issue. Manually creating different sets of buttons might be confusing.
And I have an array of temporary buttons stored in a manager (separate object).
if interfacePanel.temp_func_buttons.size() > 0:
for i in interfacePanel.temp_func_buttons.size():
interfacePanel.remove_child(interfacePanel.temp_func_buttons[i])
Before instantiating a new button I am checking this array and if itās not empty Iām deleting all the buttons from it.