Godot Version
Godot 4.3
Question
Hey everybody, I have an annoying bug.
The problem:
When I use “await” to call the Fade to Black transition screen when my character dies, it seems like the loading process overlaps the saving process (or the loading has issues).
It shows for a split second the correct amount of health (eg: I have 5 hearts: 2 filled, 3 empty), but then it turns them all empty.
Player’s alive and it seems just a visual glitch: when taking damage or collecting health, it gets fixed.
To clarify, this is the steps:
-
Player’s health reaches 0 and enters Death state (I have a State Machine)
-
Death animation finishes and calls the load_process() inside SaveManager autoload:
func _on_animated_sprite_2d_animation_finished(): if animated_sprite_2d.animation == "death": print("Player Death anim finished") player.invulnerable = false player.is_player_hit = false player.invulnerable_timer = 0 animation_finished = true Main.death_animation_finished = true SaveManager.load_process() -
Inside load_process() I use await to first call the fade to black transition before the loading process, otherwise you’d see the Player teleporting to the SavePoint.
Once that is done, the game_loaded signal fires:func load_process(): if game_is_loading: print("Save Manager: Game is already loading, ignoring duplicate call.") return print("Save Manager: Loading saved resources") game_is_loading = true await FadeTransition.hold_timer(1) await FadeTransition.fade_to_black_and_hold(2,1) # Load the saved resources from disk. _save_game = SaveGame.load_game() # Reset states from HUD _hud.reset_runtime_state() # Assign the saved values to the relative values. _player._stats = _save_game.player_resource _hud.hearts = _save_game.hud_resource.hearts _hud.hearts_health = _save_game.hud_resource.hearts_health _player.global_position = _save_game.global_position # Relocate Player to the last saved position (on a SavePoint). await get_tree().physics_frame # Wait for the game to complete a frame so hopefully signal firing won't overlap with saving. game_is_loading = false GameEvents.game_loaded.emit() -
game_loaded connects to the Player’s State Machine that transitions to Resting state where it enables the flag Main.Resting = true which is needed to save:
func enter(previous_state_path: String, data := {}) -> void: print("Player enters Resting state") FadeTransition.hold_and_fade_from_black(1, 0.5) # no await here player.animated_sprite_2d.play("resting") player.set_collision_layer_value(1, true) Main.resting = true -
if Main.resting is true and Player is inside the SavePoint area, save_process gets called from SavePoint’s _process function:
func _process(delta): if Main.resting and not SaveManager.game_is_loading: if is_player_in_area: print("Save Point: Resting received in SavePoint") SaveManager.save_process() Main.resting = false
In the Output, after Fading transtion finishes, everything gets done in a frame (a split second), and it shows first 5 hearts (2 filled hearts / 3 empty,) and then 5 empty.
Without the “await”, everything works smoothly and it only shows 2 filled / 3 empty hearts in the Output and in-game HUD.
I tried adding a wait timer after loading is done, before firing the game_loaded signal, it reduces the glitch frequency to rarely, but still happens.
To have a complete view, this is the FadeTransition’s function that gets called:
func fade_to_black_and_hold(fade_duration_in: float = 0.5, hold_duration: float = 0.5):
print("FadeTransition: Fade to black and hold")
var tween: Tween = create_tween()
tween.tween_property(color_rect, "modulate:a", 1.0, fade_duration_in)
await tween.finished
await get_tree().create_timer(hold_duration).timeout
I tried moving the await FadeTransition inside Death animation_finished, before SaveManager.load_process() is called (or elsewhere together with load_process) but nothing changes.
Any ideas? Should I separate better loading and saving processes? Maybe a Game State Machine?
Thanks for any help!