I do state appears to, because I do believe I'm missing something obvious. But I've been chasing this problem for quite a while now and I've run out of ideas.
I'm basically trying to solve the problem of transitioning between various scenes; and keeping a little bit of control over my signalling. To this end I've implemented a very basic Finite State Machine as follows:
class_name GameStateMachine
extends Node
@export var initial_state: GameState
var current_state: GameState
var _states: Dictionary = {}
func _ready() -> void:
for c in get_children():
if c is GameState:
_states[c.name.to_lower()] = c
c.transitioning.connect(_on_state_transition)
if initial_state:
current_state = initial_state
initial_state.enter()
func _process(delta: float) -> void:
if current_state:
current_state.update(delta)
func _physics_process(delta: float) -> void:
if current_state:
current_state.physics_update(delta)
func _on_state_transition(state: GameState, new_state_identifier: String) -> void:
print("transition signal from: %s" % state.name)
#SceneTransition.fade_to_black()
if state != current_state:
print("not in current state, odd")
return
print_debug("Switching State: %s to %s" % [state.name, new_state_identifier])
var new_state = _states.get(new_state_identifier.to_lower())
if not new_state:
print("Unknown state: %s" % new_state_identifier)
return
if current_state:
current_state.exit()
current_state = new_state
new_state.enter()
And my base GameState class like this.
class_name GameState
extends Node
signal transitioning
func enter() -> void:
pass
func exit() -> void:
pass
func update(delta: float) -> void:
pass
func physics_update(delta: float) -> void:
pass
The FSM kicks off in "Initial" state which looks like this.
extends GameState
func enter() -> void:
transitioning.emit(self, "title")
I know. But there's going to be some more stuff going on before kicking off the title screen; it's just not the problem I'm trying to solve right now :D
And then the "title" state, which looks like this:
extends GameState
const TITLE_SCENE: String = "res://game/main_scenes/title_screen/title_screen.tscn"
func enter() -> void:
ResourceLoader.load_threaded_request(TITLE_SCENE, "", true)
get_tree().change_scene_to_packed(ResourceLoader.load_threaded_get(TITLE_SCENE))
Very little going on, it's about as basic as it could get. Now here's the thing. I've sprinkled in a few print statements. And this is what I get:
transition signal from: Initial
Switching State: Initial to title
At: res://game/services/game_engine_core/state_machine.gd:35:_on_state_transition()
transition signal from: Initial
Switching State: Initial to title
At: res://game/services/game_engine_core/state_machine.gd:35:_on_state_transition()
And I cannot work out, HOW I end up getting that signal fired twice.
The FSM sits as a Node under my root "Game" Node which is loaded as an autoload.
Any insights as to how I can end up with double signals here? :D
Godot Version 4.1.1
Godot v4.1.1.stable - Windows 10.0.22621 - Vulkan (Compatibility) - NVIDIA GeForce RTX 4090 (NVIDIA; 31.0.15.3699) - 12th Gen Intel(R) Core(TM) i9-12900K (24 Threads)