Exporting signals
Asked Answered
F

13

0

I have a button class called PositionActivatedButton that only activates when a Node2D it is given is under it and the space bar is pressed so that i can have a sort of diegetic menu in my game. Since, from what i can gather, UIs are usually built in their own scene and then places in the game scene, currently i have to manually catch the signal from each button in the gui and re-emit it from a script on the gui scene. To counter this and also reduce coupling between the gui script and the PositionActivatedButton class, i thought about exporting a variable of type signal from the PositionActivatedButton class however i can't seem to assign the signal in the editor like i usually would and when i click it nothing happens. Is it not intended for me to be able to export signals or is it just a bug and is the use of exported signals here bad practice / is there any better way to do what i want to do?
just incase here are the scripts:

class_name LevelGui extends Control


@onready var button_holder := $ButtonHolder
@onready var score_label := $ScoreLabel
@onready var messege_label := $MessageLabel
@onready var wins_label := $WinsLabel

@onready var character_swap_button := $ButtonHolder/CharacterSwapButton


var activator: Node2D


signal play_button_pressed
signal quit_button_pressed
signal resume_button_pressed
signal settings_button_pressed
signal character_button_pressed

signal reset_file_button_pressed
signal left_bind_button_pressed
signal right_bind_button_pressed


var messege: String:
	set(new_messege):
		messege_label.text = new_messege


var score:int :
	set = set_score,
	get = get_score


var wins: int:
	set = set_wins,
	get = get_wins


func setup(new_activator: Node2D):
	for child in get_children():
		recursive_visibility_all(child, false)
	
	activator = new_activator
	for child in button_holder.get_children():
		if child is PositionActivatedButton:
			child.activator = activator


func set_score(new_score: int):
	score_label.text = str(new_score)


func get_score() -> int:
	return int(score_label.text)


func set_wins(new_wins: int):
	wins_label.text = "wins: " + str(new_wins)


func get_wins() -> int:
	return int(wins_label.text)


func on_play_button_activated(): 
	play_button_pressed.emit()


func on_quit_button_activated(): 
	quit_button_pressed.emit()


func on_resume_button_activated(): 
	resume_button_pressed.emit()


func on_settings_button_activated():
	settings_button_pressed.emit()


func on_character_button_activated():
	character_button_pressed.emit()


func on_reset_file_button_activated():
	reset_file_button_pressed.emit()


func on_left_bind_button_activated():
	left_bind_button_pressed.emit()


func on_right_bind_button_activated():
	right_bind_button_pressed.emit()


func activate_character_swap_button():
	character_swap_button.set_process(true)
	character_swap_button.visible = true


func deactivate_character_swap_button():
	character_swap_button.visible = false
	character_swap_button.set_process(false)


static func recursive_visibility_all(node: Control, state: bool):
	var children := node.get_children()
	if len(children) == 0 :
		node.visible = state
		return
	
	for child in children:
		recursive_visibility_all(child, state)
class_name PositionActivatedButton extends BaseButton


var activator: Node2D


signal activated


func _process(_delta: float) -> void:
	
	if not activator:
		return
	
	if not visible:
		return
	
	if not activator_under():
		return
	
	grab_focus()
	
	
	if not Input.is_action_just_pressed("button_press"):
		return
	
	activated.emit()


func activator_under() -> bool:
	var lb = position.x
	var ub = lb + size.x
	
	if (activator.position.x < lb)\
	or (activator.position.x > ub):
		return false
	
	return true

as you can see the gui script ends up being very verbose and repetitive which was my initial problem.

Fructuous answered 19/5, 2023 at 22:15 Comment(0)
M
0

The terminology you're using here is a bit confusing, "export" is a gdscript keyword used to expose a variable in the editor, and signal is not a variable type so you cannot export a signal. But if by "export" you mean connect the signal in the editor, then yes you should be able to do that with your custom signals the same way you would with built in signals. If you don't see your custom signals where you expect them, double check to make sure you've got the right node selected and that the correct script is attached to it.

In terms of making the code less repetitive, you can pass an argument along with the signal which could reduce the number of signals and callbacks to deal from. For example in the button script you could have something like:

signal activated(button)

@export var button_name: String
...
    activated.emit(button_name)

Then you could connect all of your buttons to the same callback in your GUI script and emit a single signal with an argument:

signal button_pressed(button)

_on_button_pressed(button_name: String) -> void:
   button_pressed.emit(button_name)
Mauser answered 19/5, 2023 at 23:1 Comment(0)
F
0

Mauser In godot 4.x signals and functions are first class objects which means that Signal is a type and i would have thought id be able to export it. I don't mean through the node tab in the editor i mean through the object inspector like any other export variable.
When i change the button class to have the following code:

@export var activated: Signal

instead of

signal activated

everything works fine and there's even a slot in the object inspector:

The issue is that when i click the exported signal field it doesnt do anything like it would if i say, exported a node.

Fructuous answered 19/5, 2023 at 23:22 Comment(0)
T
0

Fructuous How would exporting a signal help you with this?

Turley answered 19/5, 2023 at 23:30 Comment(0)
F
0

Turley It would mean i wouldn't have to embed the connections in the script gui and re-emit signals and i could just emit the signals in the gui script straight from the button. I'm trying to do something like hooking up the play_button_activated signal in the gui script straight to the play button instead of emitting the activated signal from the play button then emitting play_button_activated from the gui script

Fructuous answered 20/5, 2023 at 8:34 Comment(0)
H
0

You don't have to use the GUI. The normal way is to define them in script.

Holeproof answered 20/5, 2023 at 8:46 Comment(0)
F
0

Holeproof But then how would i connect the signals to my level script?
my scene tree looks like this:

Fructuous answered 20/5, 2023 at 9:10 Comment(0)
T
0

Fructuous You can use connect() to connect anything in the scene tree to anything else in the scene tree.

Turley answered 20/5, 2023 at 10:36 Comment(0)
F
0

Turley So is the functionality for exporting Signal type variables not meant to be there?

Fructuous answered 20/5, 2023 at 13:40 Comment(0)
T
0

Fructuous So is the functionality for exporting Signal type variables not meant to be there?

Apparently not. But even if you could set it via gui it would only assign a reference to the signal object, effectively making a signal alias. It would not make any connections.

The reasonable approach it to do what Misogynist suggested. Connect all buttons to a single callback that's in the gui's top node, and let buttons emit the signal with an argument representing the button identifier (either name or reference to button itself). Then let the top node re-emit that signal, again with identifier argument it got from button signals.

Turley answered 20/5, 2023 at 20:3 Comment(0)
H
0

I do agree that it's weird that signal exports are allowed but don't work. It could be an interesting workflow, but doesn't seem to solve any problem you can't already do with code.

Holeproof answered 20/5, 2023 at 20:45 Comment(0)
C
0

Holeproof I disagree, they could be very helpful especially for things like reusable classes or state machines.

Cohn answered 11/11, 2023 at 2:29 Comment(0)
Z
0

Fructuous can’t you expand children (editable children) and connect to the child object directly?

Edit: but I also agree a GUI signal defined on the parent is cleaner

Zagreus answered 11/11, 2023 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.