xiandie/scene/ground/ground_loader.gd

232 lines
7.4 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.

@tool
class_name GroundLoader extends Node2D
@export_group("Scene")
@export var ignore_archive := false
@export var current_scene := "c02_s01"
@export var entrance_portal := "left"
@export var debug_reload := false:
set(new_val):
debug_reload = false
if is_node_ready() and current_scene and entrance_portal:
transition_to_scene(current_scene, entrance_portal, true)
@export var archive_scene := ""
@export var archive_portal := ""
@onready var mask_layer := %MaskLayer as CanvasLayer
@onready var mask := %Mask as ColorRect
var first_entered := true
var ground: Ground2D
var display_mask_time = 0.0
var scenes_dir = "res://scene/ground/scene/"
# 场景名字映射到路径
var ground_scene_path_dict = {}
func _ready() -> void:
mask_layer.layer = GlobalConfig.CANVAS_LAYER_GROUND_MASK
mask.visible = true
mask.color.a = 0.0
# grounds
_read_grounds()
# ground = get_node_or_null("Ground")
# load save
if not ignore_archive:
_load_save()
if current_scene and entrance_portal:
transition_to_scene(current_scene, entrance_portal, true)
func _read_grounds() -> void:
# read grounds
var dir = DirAccess.open(scenes_dir)
for c_dir in dir.get_directories():
var c_path = scenes_dir + c_dir + "/"
for s_file in DirAccess.open(c_path).get_files():
if s_file.ends_with(".tscn"):
var s_path = c_path + s_file
ground_scene_path_dict[c_dir.substr(0, 3) + "_" + s_file.substr(0, 3)] = s_path
# # 确保每个 ground 都初始化 archive
# for key in ground_scene_path_dict.keys():
# if GlobalConfig.DEBUG:
# print("check ground_archive:", key)
# ArchiveManager.archive.ground_archive(key)
func _load_save():
if not Engine.is_editor_hint() and ArchiveManager.archive:
if ArchiveManager.archive.current_scene:
archive_scene = ArchiveManager.archive.current_scene
if ArchiveManager.archive.entrance_portal:
archive_portal = ArchiveManager.archive.entrance_portal
# 使用 archive 所记录的场景
if archive_scene and archive_portal:
current_scene = archive_scene
entrance_portal = archive_portal
func _toggle_mask(display: bool, _immediately: bool) -> Tween:
var tween = get_tree().create_tween()
if display:
tween.tween_property(mask, "color:a", 1.0, 0.3).set_trans(Tween.TRANS_CUBIC)
display_mask_time = Time.get_ticks_msec()
else:
# var time = Time.get_ticks_msec()
# # 转场至少 0.6s, 除去 0.3s 最后的淡出,需要 0.3s 的等待时间(包含 mask 的淡入)
# if not _immediately:
# var wait_time = max(display_mask_time + 300 - time, 0.0) * 0.001
# if wait_time:
# tween.tween_interval(wait_time)
tween.tween_property(mask, "color:a", 0.0, 0.3).set_trans(Tween.TRANS_CUBIC)
return tween
func transition_to_scene(scene_name: String, portal: String, immediately: bool) -> void:
var scene_path = ground_scene_path_dict.get(scene_name)
if scene_path:
current_scene = scene_name
entrance_portal = portal
# 优先更新 archive使 ground 可以访问自己的 current_scene 键值
_update_archive()
# 转场效果,在 _load_ground_node 之前播放
var tween = _toggle_mask(true, immediately)
tween.tween_callback(_do_transition.bind(scene_name))
tween.tween_callback(_toggle_mask.bind(false, immediately))
else:
print("Scene not found: " + scene_name)
func _update_player_position():
if ignore_archive or Engine.is_editor_hint():
return
var player = SceneManager.get_player() as MainPlayer
if player and ArchiveManager.archive:
# if GlobalConfig.DEBUG:
# print("update player position", ArchiveManager.archive.player_global_position)
if ArchiveManager.archive.player_global_position_x >= 0:
player.global_position.x = ArchiveManager.archive.player_global_position_x
player.set_facing_direction(ArchiveManager.archive.player_direction)
func _do_transition(scene_name: String):
# SceneManager.freeze_player(0)
var ground_node = _load_ground_node(scene_name)
if ground == ground_node:
return
if ground:
# 提前移除,防止命名冲突
remove_child(ground)
# 不需要释放,因为会缓存,在 ground_node_cache 中释放
# ground.queue_free()
# 先设置 ground再添加到场景中
# 因为 ground 在 enter_tree 时会用到 SceneManager 的方法
# 其中间接用到了 GroundLoader 的 ground
ground = ground_node
_add_ground()
# 预先加载邻居场景
_post_transition()
if GlobalConfig.DEBUG and not Engine.is_editor_hint():
_watch_scene_update()
func _add_ground():
ground.name = "Ground"
add_child(ground)
if not Engine.is_editor_hint():
var portal_node = ground.get_node_or_null("DeployLayer/portal_" + entrance_portal) as Node2D
if portal_node:
var player = SceneManager.get_player()
if player:
# player.global_position.x = -20.0
player.global_position.x = portal_node.global_position.x
if GlobalConfig.DEBUG:
print("move player to portal:", entrance_portal, portal_node.global_position)
else:
printerr(current_scene + " portal not found: " + entrance_portal)
# 更新玩家位置
if first_entered and not Engine.is_editor_hint():
_update_player_position()
first_entered = false
# SceneManager.release_player()
func _update_archive():
if not Engine.is_editor_hint() and ArchiveManager.archive:
ArchiveManager.archive.current_scene = current_scene
ArchiveManager.archive.entrance_portal = entrance_portal
archive_scene = current_scene
archive_portal = entrance_portal
func _load_ground_node(scene_name: String) -> Node2D:
if not ground_scene_path_dict.has(scene_name):
return null
var path = ground_scene_path_dict[scene_name]
var scene = ResourceLoader.load(path) as PackedScene
if scene:
var instance = scene.instantiate() as Node2D
var ground_node = instance.get_child(0)
instance.remove_child(ground_node)
ground_node.owner = null
instance.queue_free()
return ground_node
return null
# 读取 portals预加载邻居场景
func _post_transition():
if ground:
var scene_names = []
var deploy_layer = ground.get_node("DeployLayer")
if deploy_layer:
for node in deploy_layer.get_children():
var portal = node as Portal2D
if not portal or not portal.target_scene:
continue
if ground_scene_path_dict.has(portal.target_scene):
scene_names.append(portal.target_scene)
if scene_names:
for scene_name in scene_names:
ResourceLoader.load_threaded_request(ground_scene_path_dict[scene_name])
if GlobalConfig.DEBUG:
print("preload neighbor scenes:", scene_names)
var update_watcher: Timer
var last_modify_time = 0
# DEBUG 时重新加载资源
func _watch_scene_update():
var scene_path = ground_scene_path_dict[current_scene]
if scene_path:
last_modify_time = FileAccess.get_modified_time(scene_path)
if not update_watcher:
update_watcher = Timer.new()
update_watcher.wait_time = 1
update_watcher.one_shot = false
add_child(update_watcher)
update_watcher.start()
else:
# remove all connections
for c in update_watcher.timeout.get_connections():
update_watcher.timeout.disconnect(c.callable)
update_watcher.timeout.connect(_check_scene_update.bind(scene_path))
func _check_scene_update(scene_path):
var modify = FileAccess.get_modified_time(scene_path)
if modify != last_modify_time:
last_modify_time = modify
_on_resources_reload(scene_path)
func _on_resources_reload(res):
print("resources_reload processing:", res)
if not Engine.is_editor_hint() and res.ends_with(".tscn"):
ArchiveManager.save_all()
transition_to_scene(current_scene, entrance_portal, true)