D5/Entity/Level/LoadingScene.gd

190 lines
9.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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() # 如果错误非常严重