xiandie/scene/ground/ground_loader.gd

233 lines
7.1 KiB
GDScript3
Raw Normal View History

@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, 0.0)
# 强制覆盖 archive 记录
2025-01-21 10:52:36 +00:00
@export var force_archive_scene := ""
@export var force_archive_portal := ""
@onready var mask_layer := %MaskLayer as CanvasLayer
@onready var mask := %Mask as ColorRect
2025-01-13 08:09:57 +00:00
var first_entered := true
var ground: Ground2D
var display_mask_sec = 0.0
static func _static_init() -> void:
_read_grounds()
# 场景名字映射到路径
static var GROUND_SCENE_PATH_DICT = {}
static func _read_grounds() -> void:
var scenes_dir = "res://scene/ground/scene/"
# 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
func _ready() -> void:
mask_layer.layer = GlobalConfig.CANVAS_LAYER_GROUND_MASK
mask.visible = true
mask.color.a = 0.0
ground = get_node_or_null("Ground") as Ground2D
if ground:
ground.queue_free()
# load save
if not ignore_archive:
_load_save()
if current_scene and entrance_portal:
transition_to_scene(current_scene, entrance_portal, 0.0)
2025-01-14 00:56:51 +00:00
func _load_save():
2025-01-21 10:52:36 +00:00
# 强制覆盖 archive 记录
if force_archive_scene or force_archive_portal:
current_scene = force_archive_scene
entrance_portal = force_archive_portal
return
if not Engine.is_editor_hint():
2025-01-14 00:56:51 +00:00
if ArchiveManager.archive.current_scene:
2025-01-21 10:52:36 +00:00
current_scene = ArchiveManager.archive.current_scene
2025-01-14 00:56:51 +00:00
if ArchiveManager.archive.entrance_portal:
2025-01-21 10:52:36 +00:00
entrance_portal = ArchiveManager.archive.entrance_portal
func _toggle_mask(display: bool, mask_color: Color, wait_time: float) -> Tween:
var tween = get_tree().create_tween()
mask_color.a = mask.color.a
mask.color = mask_color
var duration = min(0.3, wait_time * 0.5)
if display:
tween.tween_property(mask, "color:a", 1.0, duration).set_trans(Tween.TRANS_CUBIC)
display_mask_sec = Time.get_ticks_msec() * 0.001
else:
# 转场至少 0.6s, 除去 0.3s 最后的淡出,需要 0.3s 的等待时间(包含 mask 的淡入)
if wait_time:
var time = Time.get_ticks_msec() * 0.001
wait_time = max(wait_time + display_mask_sec - time - 0.3, 0.0)
if wait_time:
tween.tween_interval(wait_time)
tween.tween_property(mask, "color:a", 0.0, duration).set_trans(Tween.TRANS_CUBIC)
return tween
func transition_to_scene(
scene_name: String, portal: String, wait_time := 1.0, mask_color := Color.BLACK
) -> void:
var scene_path = GROUND_SCENE_PATH_DICT.get(scene_name)
if scene_path:
current_scene = scene_name
entrance_portal = portal
2025-01-14 00:56:51 +00:00
# 优先更新 archive使 ground 可以访问自己的 current_scene 键值
if not Engine.is_editor_hint():
_update_archive()
if wait_time > 0.0:
# 转场效果,在 _load_ground_node 之前播放
var tween = _toggle_mask(true, mask_color, wait_time)
tween.tween_callback(call_deferred.bind("_do_transition", scene_name))
tween.tween_callback(_toggle_mask.bind(false, mask_color, wait_time))
else:
call_deferred("_do_transition", scene_name)
else:
print("Scene not found: " + scene_name)
func _update_archive():
ArchiveManager.archive.current_scene = current_scene
ArchiveManager.archive.entrance_portal = entrance_portal
func _do_transition(scene_name: String) -> void:
# SceneManager.freeze_player(0)
2025-01-31 10:44:30 +00:00
ground = get_node_or_null("Ground") as Ground2D
if ground:
2025-01-31 10:44:30 +00:00
# 防止命名冲突
remove_child(ground)
ground.queue_free()
# 先设置 ground再添加到场景中
# 因为 ground 在 enter_tree 时会用到 SceneManager 的方法
# 其中间接用到了 GroundLoader 的 ground
2025-01-31 10:44:30 +00:00
ground = _load_ground_node(scene_name)
_add_ground()
# 预先加载邻居场景
_post_transition()
if GlobalConfig.DEBUG and not Engine.is_editor_hint():
_watch_scene_update()
func _add_ground():
add_child(ground)
2025-01-31 10:44:30 +00:00
ground.name = "Ground"
if not Engine.is_editor_hint():
# move player to portal
ground.move_player_to_portal(entrance_portal)
# 更新玩家位置
if first_entered:
_update_player_position_from_archive()
first_entered = false
# SceneManager.release_player()
func _update_player_position_from_archive():
if ignore_archive or Engine.is_editor_hint():
return
var player = SceneManager.get_player() as MainPlayer
2025-01-21 10:52:36 +00:00
if player:
# 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 _load_ground_node(scene_name: String) -> Ground2D:
if not GROUND_SCENE_PATH_DICT.has(scene_name):
return null
var path = GROUND_SCENE_PATH_DICT[scene_name]
var scene: PackedScene
if ResourceLoader.load_threaded_get_status(path) == ResourceLoader.THREAD_LOAD_LOADED:
scene = ResourceLoader.load_threaded_get(path) as PackedScene
else:
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
2025-01-14 00:56:51 +00:00
var last_modify_time = 0
2025-01-14 00:56:51 +00:00
# DEBUG 时重新加载资源
func _watch_scene_update():
var scene_path = GROUND_SCENE_PATH_DICT[current_scene]
if scene_path:
2025-01-14 00:56:51 +00:00
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)
2025-01-14 00:56:51 +00:00
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()
2025-01-29 14:05:27 +00:00
first_entered = true
transition_to_scene(current_scene, entrance_portal, 0.0)