Godot Version
4.3
Question
` Goal of my code:
I created a Dialogue system that relies on quest progression. Basically, when you talk to an NPC, a menu appears with dialogue options: each option represent a dialogue that you can have with said NPC regarding one of the current active objectives.
The components involved:
I have a QuestManager that keeps track of the active and finished quests.
I have a Signal Bus singleton that serve as a general place I create signals destined to be emitted and connected to by multiple components (like new_state, interacted etc.)
And I have the DialogueManager proper, that handles…well dialogue stuff
How it worked so far:
In the ready function for the QuestManager, I load the save, then I use the SignalBusSingleton to emit an “update_all_quests” signal.
class_name QuestManager
extends Node
var active_quests : Array[QuestData]
var finished_quests : Array[QuestData]
var current_objectives : Array[QuestObjectiveComponent]
func _ready():
load_quests_from_save_file(true, "active_quests")
load_quests_from_save_file(true, "finished_quests")
SignalBusSingleton.update_all_quests.emit(self, active_quests, finished_quests)
#other stuff
#SignalBusSingleton.gd
extends Node
#QuestManager
##Emitters: QuestManager
##Connected: DialogueManager
signal update_all_quests(emitter : Node, active_quests : Array[QuestData], finished_quests : Array[QuestData])
#bunch of other signals
In the ready function for the DialogueManager, I connect to the “update_all_quests” signal, and in the “_on_update_all_quests” callback function, I do the things necessary to keep track of the current_objectives, so that I can select the appropriate dialogue when talking to an NPC.
class_name DialogueManager
extends Node
@export var dialogue_menu : DialogueMenu
@export var dialogue_data : DialogueData
@export var dialogue_component : DialogueComponent
var current_game_state : String
var current_quests_objectives : Array[String]
var current_dialogues : Array[DialogueData.DialogueObjectiveData]
func _ready():
SignalBusSingleton.newstate.connect(_on_newstate)
SignalBusSingleton.interacted.connect(_on_player_character_interacted)
SignalBusSingleton.update_all_quests.connect(_on_update_all_quests)
#other stuff
func _on_update_all_quests(emitter : Node, active_quests : Array[QuestData], finished_quests : Array[QuestData]) -> void:
#do stuff with active_quests
The issue:
The issue here is the DialogueManager never loads the quests’ objectives and so no dialogue menu. Annnnd…IT USED TO WORK. For real xD
What I tried:
- If I change the signal to one defined directly in QuestManager (not using SignalBusSingleton) it works. But I’d prefer to use SignalBusSingleton because I need to connect other components to that update_all_quests signal, and it’s easier not to have to aim at QuestManager
- I tried to connect to the SignalBusSingleton.update_all_quests signal in another script that runs fine, to see if the issue was DialogueManager not being properly connected…and it doesn’t work. This tells me that the signal (as writen in the above script) doesn’t emit in the firs place.
- All the other signals, that QuestManager connects to, work. So I don’t thing the issue comes from SingleBusSingleton
Any idea for debug or for a solution would be highly appreciated
Have a good day all
[EDIT] I found the issue, and it stemmed from a misunderstanding of the function _ready()
Basically, before I add a bunch of stuff in the _ready() for DialogueManager, the game just barely had the time to connect DialogueManager to the signal before the QuestManager emitted the signal.
It seems that a couple of features later, that wasn’t the case anymore and QuestManager was emitting the signal before DialogueManager had the time to connect to it.
For now, I just fixed it with band-aid, by waiting for a frame before the emit. My QuestManager script looks like this, now:
class_name QuestManager
extends Node
var active_quests : Array[QuestData]
var finished_quests : Array[QuestData]
var current_objectives : Array[QuestObjectiveComponent]
func _ready():
load_quests_from_save_file(true, "active_quests")
load_quests_from_save_file(true, "finished_quests")
#other unrelated stuff happens
#Waits for other tree to connect to update_all_quests signal
await get_tree().process_frame
SignalBusSingleton.update_all_quests.emit(self, active_quests, finished_quests)
I need to read more about the order of tree loading because I’m pretty sure this might bite me in the butt later.
Doc for await: GDScript reference — Godot Engine (stable) documentation in English
Article about tree loading: Understanding tree order :: Godot 4 Recipes
`