Struggle connecting signal of a Singleton to a node's function

Godot Version

Godot 4.3

Question

Beginner here and probably a simple solution to this problem but cannot figure it out. I have a singleton “DialogueManager”:

extends Node

@onready var text_box_scene = preload("res://Dialogue/textbox.tscn")




var dialog_lines: Array[String] = []
var current_line_index = 0

var text_box
var text_box_position: Vector2



var is_dialogue_active = false
var can_advance_line = false

signal speaking_finished

func start_dialog(position: Vector2, lines: Array[String]):
	if is_dialogue_active:
		return
	
	#var window_size = get_window().get_size_with_decorations()
	
	dialog_lines = lines
	text_box_position = position
	
	#text_box_position.x -= (38/2)
	
	
	
	_show_text_box()
	
	is_dialogue_active = true
	
	


func _show_text_box():
	text_box = text_box_scene.instantiate()
	text_box.finished_displaying.connect(_on_text_box_finished_displaying)
	get_tree().root.add_child(text_box)
	
	text_box.position = text_box_position
	
	#text_box.position = text_box_position 
	
	
	
	
	text_box.display_text(dialog_lines[current_line_index])
	can_advance_line = false
	
func _on_text_box_finished_displaying():
	can_advance_line = true
	
func _unhandled_input(event):
	if (
		event.is_action_pressed("advance_dialog") &&
		is_dialogue_active &&
		can_advance_line
	):
		text_box.queue_free()
		
		current_line_index += 1
		if current_line_index >= dialog_lines.size():
			is_dialogue_active = false
			current_line_index = 0
			speaking_finished.emit()
			return
		
		_show_text_box()



# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	pass


Most of that shouldn’t be relevant but the important part is the signal “speaking_finished”.
Then I have my player which I want to be able to access that “speaking_finished”.

extends CharacterBody2D


const SPEED = 110.0
const JUMP_VELOCITY = 0
var talking = false





const lines: Array[String] = [
	"Howdy, there. I'm cowboy chameleon.",
	"This is test dialogue."
]

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var collision_shape: CollisionShape2D = $CollisionShape2D




func _unhandled_input(event: InputEvent) -> void:
	
	if event.is_action_pressed("interact"):
		talking = true
		DialogueManager.start_dialog(collision_shape.global_position, lines)
		
		

func _on_speaking_finished():
	talking = false
	
	

func _physics_process(delta: float) -> void:
	# Add the gravity.
	
	DialogueManager.speaking_finished.connect(_on_speaking_finished())
	
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY
		
		
	 
	
	

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	
	
	var direction := Input.get_axis("move_left", "move_right")
	
	if talking:
		animated_sprite.play("talk")
	else:
		if direction > 0:
			animated_sprite.flip_h = false
		elif direction < 0:
			animated_sprite.flip_h = true
	
	# Play walk animation
		if direction == 0:
			animated_sprite.play("idle")
		else:
			animated_sprite.play("walk")	
	
		if direction:
			velocity.x = direction * SPEED
		else:
			velocity.x = move_toward(velocity.x, 0, SPEED)
	
	# Flips the sprite to face the right direction
		if direction > 0:
			animated_sprite.flip_h = false
		elif direction < 0:
			animated_sprite.flip_h = true
	
	# Play walk animation
		if direction == 0:
			animated_sprite.play("idle")
		else:
			animated_sprite.play("walk")	
	
		if direction:
			velocity.x = direction * SPEED
		else:
			velocity.x = move_toward(velocity.x, 0, SPEED)

	move_and_slide()

I can access the DialogueManager and call start_dialogue fine but I want to connect the signal so that the Player class is informed when the dialogue is finished. I get the following error when running the code as this:

Invalid type in function ‘connect’ in base ‘Signal’. Cannot convert argument 1 from Nil to Callable.

I’ve tried placing this line elsewhere in the player, instantiating a dialogue manager within the player and using that and a number of other things but cannot get it to work. Sorry if this is some obvious mistake on my part, thanks for reading

You have to remove the () when connecting the signal:

DialogueManager.speaking_finished.connect(_on_speaking_finished)

When using() you are calling the function and passing the return value to connect, but you want to pass fhe function itself.

You also probalby don’t want to do that every physics tick (you can only connect a callable once) but do it once (e.g., in _ready).

2 Likes

thankyou. I did think ready would be the place to do it, just moved it around a bunch trying to see if it would work. thankyou very much, i’ve got it working now