190 lines
9.1 KiB
GDScript
190 lines
9.1 KiB
GDScript
# LoadingScene.gd
|
||
# 负责驱动加载流程,更新UI,并在完成后切换到游戏主场景。
|
||
|
||
extends Control
|
||
|
||
# --- 节点引用 ---
|
||
@onready var progress_bar: ProgressBar = $MainContainer/LoadingProgressBar
|
||
@onready var loading_label: Label = $MainContainer/LoadingLabel
|
||
|
||
# --- 常量 ---
|
||
const GAME_SCENE_PATH = "res://Entity/Level/GameScene.tscn" # 请确保这个路径是正确的!
|
||
|
||
# --- 内部状态变量 ---
|
||
var _current_action: String = ""
|
||
var _save_data_to_process: Dictionary = {}
|
||
|
||
var _target_animation_progress: float = 0.0 # 进度条动画的目标值 (用于20%-80%的平滑过渡)
|
||
var _current_animation_progress: float = 0.0 # 进度条动画的当前值
|
||
var _animation_speed: float = 30.0 # 进度条动画速度 (百分比/秒),可以根据需要调整
|
||
|
||
var _initialization_started: bool = false # 标记核心初始化流程是否已开始
|
||
var _initialization_complete: bool = false # 标记核心初始化流程是否已完成
|
||
|
||
|
||
func _ready():
|
||
print("LoadingScene: Scene Ready.")
|
||
# 1. 初始化UI
|
||
_update_progress(0.0, "准备加载...")
|
||
|
||
# 2. 从 LoadingContext 获取参数
|
||
if not ProjectSettings.has_setting("autoload/LoadingContext"):
|
||
_handle_critical_error("LoadingContext Autoload 未配置!")
|
||
return
|
||
|
||
_current_action = LoadingContext.action_to_perform
|
||
# 对存档数据进行深拷贝,以防万一LoadingContext中的原始数据被意外修改
|
||
_save_data_to_process = LoadingContext.save_data_for_loading.duplicate(true)
|
||
|
||
# 3. 清空 LoadingContext (这是一个好习惯)
|
||
LoadingContext.clear_context()
|
||
|
||
# 验证获取到的action是否有效
|
||
if _current_action != "new_game" and _current_action != "load_game":
|
||
_handle_critical_error("无效的加载指令: " + _current_action)
|
||
return
|
||
|
||
print("LoadingScene: Action to perform - ", _current_action)
|
||
if _current_action == "load_game":
|
||
print("LoadingScene: Save data keys to process - ", _save_data_to_process.keys())
|
||
|
||
# 4. 更新进度到阶段1 (10%)
|
||
_update_progress(10.0, "加载参数读取完毕...")
|
||
|
||
# 5. 连接 InitializationManager 的信号
|
||
# 使用 CONNECT_ONE_SHOT 确保信号只触发一次,处理后自动断开,避免重复处理或内存泄漏
|
||
if not ProjectSettings.has_setting("autoload/InitializationManager"):
|
||
_handle_critical_error("InitializationManager Autoload 未配置!")
|
||
return
|
||
|
||
if _current_action == "new_game":
|
||
if InitializationManager.is_connected("new_game_initialized", Callable(self, "_on_initialization_manager_finished")):
|
||
printerr("LoadingScene: Warning - new_game_initialized signal already connected by this instance. This might indicate a logic error if _ready is called multiple times unexpectedly.")
|
||
else:
|
||
InitializationManager.new_game_initialized.connect(self._on_initialization_manager_finished, CONNECT_ONE_SHOT)
|
||
print("LoadingScene: Connected to InitializationManager.new_game_initialized.")
|
||
elif _current_action == "load_game":
|
||
if InitializationManager.is_connected("game_loaded", Callable(self, "_on_initialization_manager_finished")):
|
||
printerr("LoadingScene: Warning - game_loaded signal already connected by this instance.")
|
||
else:
|
||
InitializationManager.game_loaded.connect(self._on_initialization_manager_finished, CONNECT_ONE_SHOT)
|
||
print("LoadingScene: Connected to InitializationManager.game_loaded.")
|
||
|
||
# 6. 使用 call_deferred 延迟调用实际的初始化启动函数
|
||
# 这可以确保当前 _ready() 函数中的UI更新等操作完成后再开始耗时操作。
|
||
call_deferred("_start_actual_initialization")
|
||
|
||
|
||
func _start_actual_initialization() -> void:
|
||
print("LoadingScene: Starting actual initialization...")
|
||
# 更新进度到阶段2 (20%)
|
||
_update_progress(20.0, "开始初始化核心数据...")
|
||
|
||
_initialization_started = true
|
||
_current_animation_progress = 20.0 # 动画从20%开始
|
||
_target_animation_progress = 80.0 # 动画目标到80%
|
||
|
||
var success: bool = false
|
||
if _current_action == "new_game":
|
||
success = InitializationManager.start_new_game_process()
|
||
if not success:
|
||
_handle_initialization_failure("新游戏流程启动失败 (InitializationManager.start_new_game_process 返回 false)。")
|
||
elif _current_action == "load_game":
|
||
if _save_data_to_process.is_empty() and _current_action == "load_game":
|
||
# 这是一个潜在问题,如果我们要加载游戏,但没有存档数据
|
||
_handle_initialization_failure("加载游戏失败:没有提供有效的存档数据。")
|
||
return
|
||
success = InitializationManager.load_game_process(_save_data_to_process)
|
||
if not success:
|
||
_handle_initialization_failure("加载游戏流程启动失败 (InitializationManager.load_game_process 返回 false)。")
|
||
|
||
if success:
|
||
print("LoadingScene: Initialization process started via InitializationManager.")
|
||
# 如果 success 为 false,_handle_initialization_failure 已经被调用
|
||
|
||
|
||
func _process(delta: float) -> void:
|
||
# 如果核心初始化已开始且未完成,则驱动进度条动画
|
||
if _initialization_started and not _initialization_complete:
|
||
if _current_animation_progress < _target_animation_progress:
|
||
_current_animation_progress = move_toward(_current_animation_progress, _target_animation_progress, _animation_speed * delta)
|
||
# 只更新进度条的值,不频繁更新文本,避免性能开销和视觉跳动
|
||
progress_bar.value = _current_animation_progress
|
||
# (可选) 如果动画已达到80%但仍在等待信号,可以添加一个小的等待动画或文本提示
|
||
# else:
|
||
# loading_label.text = "正在等待核心数据处理完成..." (确保只设置一次)
|
||
|
||
|
||
# 当 InitializationManager 完成新游戏或加载游戏流程后,此函数被调用
|
||
func _on_initialization_manager_finished() -> void: # success 参数已移除,因为信号发出即代表成功
|
||
print("LoadingScene: Received signal from InitializationManager (initialization finished).")
|
||
_initialization_complete = true
|
||
|
||
# 确保动画进度至少达到目标,或者直接跳到90%
|
||
_current_animation_progress = max(_current_animation_progress, _target_animation_progress)
|
||
|
||
# 更新进度到阶段3 (90%)
|
||
_update_progress(90.0, "核心数据准备完毕!")
|
||
|
||
# 【增加延时】在切换到游戏场景之前
|
||
#print("LoadingScene: Adding a DEBUG delay before transitioning to GameScene...")
|
||
#await get_tree().create_timer(3.0).timeout # <--- 增加3秒延时 (用于调试)
|
||
#print("LoadingScene: DEBUG delay finished.")
|
||
|
||
# 延迟切换到游戏场景
|
||
call_deferred("_transition_to_game_scene")
|
||
|
||
|
||
func _transition_to_game_scene() -> void:
|
||
print("LoadingScene: Preparing to transition to GameScene...")
|
||
# 更新进度到阶段4 (100%)
|
||
_update_progress(100.0, "正在进入游戏...")
|
||
|
||
# (可选) 给用户一个非常短暂的时间看到100%
|
||
# await get_tree().create_timer(0.1).timeout
|
||
# 注意: 上面的 await 会使函数变成异步,如果后续有更多同步代码,需要注意。
|
||
# 对于简单的场景切换,直接调用通常没问题。
|
||
|
||
var err = get_tree().change_scene_to_file(GAME_SCENE_PATH)
|
||
if err != OK:
|
||
_handle_critical_error("切换到 GameScene 失败! 错误码: " + str(err) + "路径: " + GAME_SCENE_PATH)
|
||
# 此时游戏可能处于一个无法恢复的状态,可以考虑显示一个永久的错误信息
|
||
|
||
|
||
# --- 辅助函数 ---
|
||
|
||
func _update_progress(value: float, message: String = "") -> void:
|
||
progress_bar.value = value
|
||
if not message.is_empty():
|
||
# 为了避免过于频繁地更新Label内容导致潜在的性能问题或闪烁,
|
||
# 我们可以只在关键阶段更新文本,或者确保文本内容确实改变了再更新。
|
||
# 这里我们假设在关键阶段更新文本是可以接受的。
|
||
loading_label.text = message # +" " + str(int(value)) + "%"
|
||
# ProgressBar的show_percentage=true会自动显示百分比
|
||
print("LoadingScene: Progress updated - ", int(value), "% - ", message)
|
||
|
||
|
||
func _handle_initialization_failure(error_message: String) -> void:
|
||
printerr("LoadingScene: Initialization Failed - ", error_message)
|
||
_initialization_started = false # 停止任何进行中的动画逻辑
|
||
_initialization_complete = true # 标记流程结束(虽然是失败的结束)
|
||
|
||
# 更新UI以反映错误状态
|
||
progress_bar.value = progress_bar.min_value # 或者一个特定的错误值/样式
|
||
# 可以考虑给ProgressBar添加一个自定义主题,当发生错误时改变其颜色
|
||
# progress_bar.add_theme_stylebox_override("fill", preload("res://error_progress_bar_fill_style.tres"))
|
||
loading_label.text = "错误: " + error_message + "\n请尝试返回主菜单或重启游戏。"
|
||
# 在这里,可以考虑启用一个“返回主菜单”的按钮,或者其他用户可进行的操作。
|
||
# 例如: get_node("Path/To/RetryButton").disabled = false
|
||
|
||
|
||
func _handle_critical_error(error_message: String) -> void:
|
||
printerr("LoadingScene: CRITICAL ERROR - ", error_message)
|
||
_initialization_started = false
|
||
_initialization_complete = true # 阻止进一步操作
|
||
if progress_bar: progress_bar.value = 0
|
||
if loading_label: loading_label.text = "严重错误: " + error_message + "\n游戏可能无法继续。"
|
||
# 对于严重错误,可能除了显示信息外做不了太多
|
||
# 可以禁用所有交互,或者尝试安全退出游戏
|
||
# get_tree().quit() # 如果错误非常严重
|