Differentiating objects that are sharing same script

Godot Version

4.2

Question

I have two object on my scene, that are sharing same code. When I am instantiating those on my scene, I am assigning them a unique ID in _ready func:

func _ready():
    
    # Setting values. 
    houseid = randi() % 50

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:

image
image
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:

image

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!

Thanks in advance for any advice :slight_smile:

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:

image

And I’m overwriting them each time I click on building. I thought that it will overwrite the funcition as well in a sense.

So, essentially, you’re doing this, right?

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!

1 Like

func _on_building_static_input_event(viewport, event, shape_idx):

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.

1 Like

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.

1 Like

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.

I prefer using two (or more) buttons and according to the scenario, hide/show the proper one(s).

It’s also easier to work with, debug, etc. At least for me it is.

1 Like

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.

So here is the solution, thanks to @njamster. I decided to try again an approach of creating a separate button each time.

Each time I’m clicking on building, this happens:

var func_button = Button.new() 
func_button.set_text(Cut + ' ' + str(houseid))|
func_button.position.x = interfacePanel.get_child(4).position.x|
func_button.position.y = interfacePanel.get_child(4).position.y|
interfacePanel.add_child(func_button)|
interfacePanel.temp_func_buttons.append(func_button)|
func_button.visible = true|
func_button.pressed.connect(CutArea)|

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.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.