Await global signals?

Godot Version

v4.2.2 stable

Question

How do I await a signal that goes through a signal bus? Maybe I am using the wrong way to do this, but I have a node that talks to a global signal bus and tells it some things about dialogue, and then there is another node that is supposed to take the dialogue and say it, but I have to await it because it just ignores the time it takes and continues executing a method. The problem is that it doesn’t let me await and gives me this “Trying to get a return value of a method that returns ‘void’” error. The strange thing is, if I make the dialogue speaker emit the signal locally, it works just fine and I can await it. Any reason that it does this?

Can you post the script and point out the problematic line? There shouldn’t be any difference in signals global or otherwise. It sounds like the method being called either doesn’t have a delay and cannot be awaited, or something is trying to be set to a void function’s return value.

await SignalBus.speak.emit(["Hello!"], "normal")

Then in signal bus:

signal speak(words: Array, expression: String)

Emitting signals is a void function, there isn’t anything to await on; it’s firing it into the ether. Maybe you want a more tightly coupled speak function

I just need something that I can call from anywhere. Either I do that or I have to go like “$…/Thingy” which I heard is bad practice.

you connect to signal and your signal can be called anywhere. you can use exports to connect nodes with inspector.

I would agree going up the tree with $"../" is bad, @export is great for handling “up the tree” situations. I also believe signal busses are a bad practice, less global signals the better.

Why do you need it to call from anywhere? I figure “say” should be called on the few things that can talk, they could put their words on an Autoloaded dialogue scene, then everything is on a closed loop and only loaded once.

Then how could I make a way for the actions in the game to get a character to speak? The character has it’s own node (it IS the HUD, actually).

First thing I think of is using area. I’ll demonstrate how I believe a press “e” to talk could work

extends Area2D

@export var talker: Talker
@onready var label: Label = $Label

func _on_body_entered(body: Node2D) -> void:
    if body is Player:
        set_process_input(true)
        label.show()

func _on_body_exited(body: Node2D) -> void:
    if body is Player:
        set_process_input(false)
        label.hide()

func _input(event: InputEvent) -> void:
    if event.is_action_pressed("Interact"):
        talker.start_dialogue()

        # don't take input again until dialogue is over.
        set_process_input(false)

        # if dialogue is handled by a Autoloaded scene
        await DialogueUI.dialogue_ended

        # if dialogue is handled as part of the talker's UI
        await talker.dialogue_ended

        set_process_input(true)

And in this world here’s how that Talker script would look

extends Node2D
class_name Talker

# again if no Autoloaded dialogue scene
signal dialogue_ended

func start_dialogue():
    pass # say a word until dialoge end

How can I export a function? I don’t know if this is what you’re referring to.

It isn’t exactly an RPG… Let’s just say, I need it to behave with minesweeper’s mechanics.

Signals with no objects don’t exits. if you have your own object you can call any function

I can’t really give specifics without some specific code to adapt to, but I think we’re getting into the weeds of it anyways. The main thing to consider is using a start_dialogue function on whatever object stores talking data; usually whomever is speaking, or it’s data in the level.

1 Like

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