Godot Version 4.2.1
Intro
I have SceneManager.gd that using load_threaded_request() for loading screen. New scene starts to load after player leave scene. I took this manager from here:
(also code below)
But now I need to load scenes while active gameplay.
So I’m planning to make another system that will look which levels are connected to current and load_threaded_request them on _ready().
Systems will work independently.
QUESTIONS
Is it okay if I use load_threaded_request("level 1.tscn") two times?
First in _ready() and second on transition in SceneManager.gd.
What would happen if loading in _ready() doesn’t finish, and I try to switch levels, so another load_threaded_request("level 1.tscn") will be called?
Maybe I should modify SceneManger.gd, so there wont be two same calls of load_threaded_request? But I would prefer to avoid SceneManger.gd modifying.
SceneManager.gd
extends Node
# Bacon and Games on YouTube: https://www.youtube.com/watch?v=2uYaoQj_6o0
const LEVEL_H:int = 144
const LEVEL_W:int = 240
signal content_finished_loading(content)
signal zelda_content_finished_loading(content)
signal content_invalid(content_path:String)
signal content_failed_to_load(content_path:String)
var loading_screen:LoadingScreen
var _loading_screen_scene:PackedScene = preload("res://scenes/loading_screen.tscn")
var _transition:String
var _content_path:String
var _load_progress_timer:Timer
var change_on_sate = GLOBAL.states.game # переключение на состояние после загруззки
func _ready() -> void:
content_invalid.connect(on_content_invalid)
content_failed_to_load.connect(on_content_failed_to_load)
content_finished_loading.connect(on_content_finished_loading)
func load_new_scene(content_path:String, transition_type := "fade_to_black", state=GLOBAL.states.game) -> void:
change_on_sate = state
_transition = transition_type
# add loading screen
loading_screen = _loading_screen_scene.instantiate() as LoadingScreen
get_tree().root.add_child(loading_screen)
loading_screen.start_transition(transition_type)
_load_content(content_path)
func load_level_zelda(content_path:String) -> void:
_transition = "zelda"
_load_content(content_path)
func _load_content(content_path:String) -> void:
# zelda transition doesn't use a loading screen - personal preference
if loading_screen != null:
await loading_screen.transition_in_complete
_content_path = content_path
var loader = ResourceLoader.load_threaded_request(content_path)
if not ResourceLoader.exists(content_path) or loader == null:
content_invalid.emit(content_path)
return
_load_progress_timer = Timer.new()
_load_progress_timer.wait_time = 0.1
_load_progress_timer.timeout.connect(monitor_load_status)
get_tree().root.add_child(_load_progress_timer)
_load_progress_timer.start()
# checks in on loading status - this can also be done with a while loop, but I found that ran too fast
# and ended up skipping over the loading display.
func monitor_load_status() -> void:
var load_progress = []
var load_status = ResourceLoader.load_threaded_get_status(_content_path, load_progress)
match load_status:
ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
content_invalid.emit(_content_path)
_load_progress_timer.stop()
return
ResourceLoader.THREAD_LOAD_IN_PROGRESS:
if loading_screen != null:
loading_screen.update_bar(load_progress[0] * 100) # 0.1
ResourceLoader.THREAD_LOAD_FAILED:
content_failed_to_load.emit(_content_path)
_load_progress_timer.stop()
return
ResourceLoader.THREAD_LOAD_LOADED:
_load_progress_timer.stop()
_load_progress_timer.queue_free()
if _transition == "zelda":
zelda_content_finished_loading.emit(ResourceLoader.load_threaded_get(_content_path).instantiate())
else:
content_finished_loading.emit(ResourceLoader.load_threaded_get(_content_path).instantiate())
return # this last return isn't necessary but I like how the 3 dead ends stand out as similar
func on_content_failed_to_load(path:String) -> void:
printerr("error: Failed to load resource: '%s'" % [path])
func on_content_invalid(path:String) -> void:
printerr("error: Cannot load resource: '%s'" % [path])
func on_content_finished_loading(content) -> void:
# Remove current scene
get_tree().current_scene.queue_free()
# Add and set the new scene to current
get_tree().root.call_deferred("add_child",content)
get_tree().set_deferred("current_scene",content)
GLOBAL.current_game_state = change_on_sate
# probably not necssary since we split our content_finished_loading but it won't hurt to have an extra check
if loading_screen != null:
loading_screen.finish_transition()
# wait for LoadingScreen's transition to finish playing
await loading_screen.anim_player.animation_finished
loading_screen = null
# load in a level, does NOT use the loading screen (which comes with tradeoffs)