Trouble with Signalling Events and Variables from Instantiated Nodes

Godot 4.1.1

I’m not quite a complete novice, but I am self-taught, which is always dangerous. I’ve read as many forum postings on this as I can find, but as far as I can make out, I can’t adapt these to tackle my issues around ‘pressed’ events or signalled variables.

I want to instantiate a series of StaticBody3D nodes and connect a signal from each of them to an auto-loaded global script so that they are mouse-selectable. Crucially, when selected they need to emit a signal of their position and a string for their ‘name’.

I’ve looked at the docs. The “Connecting a signal via code” section has a timer signal as an example, adapting this code slightly makes it work for an instantiated timer. Off to a good start then:

extends Node3D

var timer_node = preload(“res://timer.tscn”)

func _ready():
var timer = timer_node.instantiate()
add_child(timer)
timer.timeout.connect(_on_timer_timeout)

func _on_timer_timeout():
print(“timed_out”)

My own signalling works fine when the nodes are already in the scene tree, and the signal connections are made from the front-end interface, which auto-generates code on the target node’s script which can then be expanded on:

func _on_item_input_event(_camera, event, position, _normal, _shape_idx, extra_arg_0, extra_arg_1) → void:
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed == true:
var item_position = Vector3i(round(position.x), round(position.y), round(position.z))
var item_name = str(_extra_arg_0 + " " + extra_arg_1)
do_stuff(item_name, item_position)

But I absolutely have to be able to do this same signalling from instantiated nodes, the alternative is creating hundreds of nodes in the scene tree and giving each one a specific and unique name - with no typos! So the code below is as close as I’ve got, it doesn’t crash, but it doesn’t print my test message either:

extends Node3D

var item_node = preload(“res://item.tscn”)

func create_items() → void:
var item_count: = 0
var item_position: = Vector3i(some vector)
var item_name: = “some name”
while item_count < (some number):
var item = item_node.instantiate()
add_child(item)
item.position = item_position
item.input_event.connect(on_create_items_item_selected, 0)
item_count: += 1
item_position: = Vector3i(some new vector)
item_name: = “some new name”

func on_create_items_item_selected():
print(“it got this far”)

Where, oh where, does ‘MOUSE_BUTTON_LEFT’ go?

More broadly, the .input_event.connect only accepts two parameters (the last of which is an integer whose purpose is beyond me). The front-end auto-generated code has (amongst other things) an event, a position, and as many extra string arguments as you care to define. Even if it did get my code to print that “it got this far”, how will the instantiated nodes ever be able to signal their position and name?

Any help would be greatly, greatly appreciated.

I don’t know why the tab indents in my code didn’t copy across.

You’ll want to use preformatted text, otherwise the html eats extra spaces. It’s the </> icon in the editor, which inserts three backticks (`) in a row to begin and end it.

type or paste code here

I’ve made a little progress, if I attach this script to the item node to be instanced then I get the test message to print!

extends StaticBody3D

@onready var item_name= “some name

signal emit_item_selected(item_name, item_position)

func item_selected() → void:
var item_position = self.global_position
emit_signal("emit_item_selected", item_name, item_position)
print("this is on the instanced scene!")

… and then in the parent node, once the .instantiate row has run, I use this line:
item.mouse_entered.connect(item.piece_selected)

Unfortunately, when I replace “mouse_entered” with “input_event” I get error messages when I hover over the items, whic all say:
“Error calling from signal ‘input_event’ to callable: ‘StaticBody3D(item.gd)::item_selected’: Method expected 0 arguments, but called with 5.”.

Those 5 arguments will be the five from connecting the “input_event” signal directly when it’s already in the scene tree (i.e. not instantiated by code):
func _on_item_input_event(_camera, event, position, _normal, _shape_idx, extra_arg_0, extra_arg_1)

So same questions as before, how do you make ‘pressed’ work? How do you signal the name and position?

Please help.

Many thanks, but as you can see below, I still haven’t got it quite right! (I’ll keep practicing.)

I mean above!

Hi @athousandships

I’ve seen that you’ve helped out on (How to connect a signal to an instanced scene).

Would you mind taking a look at this one? Perhaps you have some ideas…

Please edit your code so it can be read easily and i’ll take a look

10 4 chief, let me work on that.

On the Parent Node:

extends Node3D

var item_node = preload(“res://item.tscn”)

func create_items() → void:
    var item_count: = 0
    var item_position: = Vector3i(some vector)
    var item_name: = “some name”
    while item_count < (some number):
        var item = item_node.instantiate()
        add_child(item)
        item.position = item_position
        item.mouse_entered.connect(item.piece_selected)
        item_count: += 1
        item_position: = Vector3i(some new vector)
        item_name: = “some new name”

func on_create_items_item_selected():
    print(“it got this far”)

On the Instanced Child:

extends StaticBody3D

@onready var item_name= “some name”

signal emit_item_selected(item_name, item_position)

func item_selected() → void:
    var item_position = self.global_position
    emit_signal("emit_item_selected", item_name, item_position)
    print("this is on the instanced scene!")

It worked! Phew! That took me a while for something so easy.

So anyway, the basic problem, how do I get the instanced node to signal it’s position and name when it’s mouse selected?

Really appreciate any help.

Will take a look today :slight_smile:

You should read the docs on signals, and just leave it with it’s default setting.

Well the item that is selected needs to override the notification event of the input as you have already done, then emit a new signal with your defined parameters.

signal my_input_event(node_position:vector3, node_name: String)


Func _input_event ( Camera3D camera, InputEvent event, Vector3 position, Vector3 normal, int shape_idx ):
  ...
  My_input_event.emit(position, name)
  ...
The autoload

item.my_input_event.connect(input_func)

Func input_funce(node_position: vector3, node_name:string)
  ...

For the input you need to utilize the notification function

Read point 8

2 Likes

Since input_event has a separate signal you could short cut the function you just need to connect it to a function with all the parameters the signal contains. But signals won’t have the instance of the object it came from. So it requires you to make a custom signal with identity parameter.

1 Like

Hi @pennyloafers (and @athousandships too, I know you were going to get back to me),

It worked!

I can see how it all fits together now, and what needs to go where.
Many many thanks.

For those that might look at this thread in the future, here’s the final code signalling properly.

On the Instanced Node:

extends StaticBody3D

@onready var item_name: String = "some_name"

signal signal_item_selected(item_name: String, item_position: Vector3i)

func _input_event(_camera: Camera3D, _event: InputEvent, _position: Vector3, _normal: Vector3, _shape_idx: int) -> void:
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT and event.pressed == true:
			var item_position: Vector3i = self.global_position
			signal_item_selected.emit(item_name, item_position)

On the Parent Node:

extends StaticBody3D

var item_count = 0

func create_items() -> void:
	var item_position = Vector(some_vector)
	while item_count < some_number:
		var item = item_node.instantiate()
		add_child(item)
		var item_name = "some_name”
		item.add_to_group(item_name)
		item.signal_item_selected.connect(item_selected)
		item.position = item_position
		item_position = Vector(some_new_vector)
		item_name = “some_new_name”
		item_count  += 1


func item_selected(item_name: String, item_position: Vector3i):
	print(item_name, item_position)
1 Like

Happy you got answers! Was going to get back to this but since others responded I let them take it :slight_smile:

1 Like