xiandie/scene/ground/ground_loader.gd
2025-05-21 19:03:44 +08:00

239 lines
8.2 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, 0.0)
# 强制覆盖 archive 记录
@export var force_archive_scene := ""
@export var force_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_sec = 0.0
# 场景名字映射到路径
static var GROUND_SCENE_PATH_DICT = {
"c01_s05": "res://scene/ground/scene/c01/s05_院长房间.tscn",
"c01_s06": "res://scene/ground/scene/c01/s06_孤儿院长廊围墙.tscn",
"c01_s07": "res://scene/ground/scene/c01/s07_书店外.tscn",
"c01_s08": "res://scene/ground/scene/c01/s08_书店.tscn",
"c01_s09": "res://scene/ground/scene/c01/s09_公寓楼外.tscn",
"c01_s10": "res://scene/ground/scene/c01/s10_公寓楼道.tscn",
"c01_s11": "res://scene/ground/scene/c01/s11_黄包车演出.tscn",
"c01_s12": "res://scene/ground/scene/c01/s12_书店外_诡异版.tscn",
"c02_s01": "res://scene/ground/scene/c02/s01_街道.tscn",
"c02_s02": "res://scene/ground/scene/c02/s02_过道.tscn",
"c02_s03": "res://scene/ground/scene/c02/s03_院子.tscn",
"c02_s04": "res://scene/ground/scene/c02/s04_保卫科.tscn",
"c02_s05": "res://scene/ground/scene/c02/s05_一楼内侧楼道.tscn",
"c02_s06": "res://scene/ground/scene/c02/s06_二楼.tscn",
"c02_s07": "res://scene/ground/scene/c02/s07_二楼内侧楼道.tscn",
"c02_s08": "res://scene/ground/scene/c02/s08_瞎子卧室.tscn",
"c02_s09": "res://scene/ground/scene/c02/s09_裂缝.tscn",
"c02_s10": "res://scene/ground/scene/c02/s10_空房间.tscn",
"c02_s11": "res://scene/ground/scene/c02/s11_一楼火灾.tscn", # 注:该场景合并在了 c02_s03 院子中
# "c02_s12": "res://scene/ground/scene/c02/s12_1014诡异版.tscn",
# "c02_s13": "res://scene/ground/scene/c02/s13_院子诡异版.tscn",
# "c02_s14": "res://scene/ground/scene/c02/s14_过道诡异版.tscn",
# "c02_s15": "res://scene/ground/scene/c02/s15_1014家具版.tscn",
}
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)
func _load_save():
# 强制覆盖 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():
if ArchiveManager.archive.current_scene:
current_scene = ArchiveManager.archive.current_scene
if ArchiveManager.archive.entrance_portal:
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.4, 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
# 优先更新 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)
ground = get_node_or_null("Ground") as Ground2D
if ground:
# 防止命名冲突
remove_child(ground)
ground.queue_free()
# 先设置 ground再添加到场景中
# 因为 ground 在 enter_tree 时会用到 SceneManager 的方法
# 其中间接用到了 GroundLoader 的 ground
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)
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
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
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()
first_entered = true
transition_to_scene(current_scene, entrance_portal, 0.0)