GroundLoader 实现预加载邻居场景;完善转场效果;全局控制 player 奔跑锁定

This commit is contained in:
cakipaul 2025-01-21 15:58:21 +08:00
parent 4c4566c8d8
commit 5e0183a8a4
28 changed files with 304 additions and 72 deletions

View File

@ -97,10 +97,10 @@ func _import(source_file, save_path, options, platform_variants, gen_files):
var image = texture.get_image() var image = texture.get_image()
var image_path = dir_name + str(i) + ".png" var image_path = dir_name + str(i) + ".png"
image.save_png(image_path) image.save_png(image_path)
image.resource_path = image_path texture = ImageTexture.create_from_image(image)
var new_texture = ImageTexture.create_from_image(image) texture.take_over_path(image_path)
var duration = frames.get_frame_duration("gif", i) var duration = frames.get_frame_duration("gif", i)
sprite_frames.add_frame(animation_name, new_texture, duration) sprite_frames.add_frame(animation_name, texture, duration)
print_debug("Created images and added to SpriteFrames: ", animation_name) print_debug("Created images and added to SpriteFrames: ", animation_name)
ResourceSaver.save(sprite_frames) ResourceSaver.save(sprite_frames)
return code return code

View File

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

@ -3,15 +3,15 @@
importer="texture" importer="texture"
type="CompressedTexture2D" type="CompressedTexture2D"
uid="uid://cgghff16powfg" uid="uid://cgghff16powfg"
path="res://.godot/imported/prop遮罩.png-f0336d91e6b2ddd4060007c0fc87eea0.ctex" path="res://.godot/imported/prop遮罩.png-85fb17d3ccb28d9d3887ddc81281a85f.ctex"
metadata={ metadata={
"vram_texture": false "vram_texture": false
} }
[deps] [deps]
source_file="res://asset/art/ui/prop遮罩.png" source_file="res://asset/art/ui/prop/prop遮罩.png"
dest_files=["res://.godot/imported/prop遮罩.png-f0336d91e6b2ddd4060007c0fc87eea0.ctex"] dest_files=["res://.godot/imported/prop遮罩.png-85fb17d3ccb28d9d3887ddc81281a85f.ctex"]
[params] [params]

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://f186lvt5y2ql"
path="res://.godot/imported/遮罩.png-dab9f7c60166ce0e17692f077a460bfb.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://asset/art/ui/prop/遮罩.png"
dest_files=["res://.godot/imported/遮罩.png-dab9f7c60166ce0e17692f077a460bfb.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -152,9 +152,10 @@ func _create_mirror():
var flipped_image = mirrored_frame.get_image() as Image var flipped_image = mirrored_frame.get_image() as Image
flipped_image.flip_x() flipped_image.flip_x()
var flipped_img_path = mirror_dir_path + "/" + id + ".png" var flipped_img_path = mirror_dir_path + "/" + id + ".png"
flipped_image.resource_path = flipped_img_path
flipped_image.save_png(flipped_img_path) flipped_image.save_png(flipped_img_path)
sprite_frames.add_frame(mirror_mapping, load(flipped_img_path)) var texture = ImageTexture.create_from_image(flipped_image)
texture.take_over_path(flipped_img_path)
sprite_frames.add_frame(mirror_mapping, texture)
sprite_frames.set_animation_speed(mapping_name, frames_per_sec) sprite_frames.set_animation_speed(mapping_name, frames_per_sec)

View File

@ -3,9 +3,8 @@ extends Node
signal archive_loaded signal archive_loaded
static var archive := ( static var archive: AssembledArchive
load("user://data/archives/save0" + GlobalConfig.RES_FILE_FORMAT) as AssembledArchive # current archive
) # current archive
static var user_root_dir := "user://data/" # must end with "/" static var user_root_dir := "user://data/" # must end with "/"
static var archive_dir := "user://data/archives/" static var archive_dir := "user://data/archives/"
static var archive_prefix := "save" static var archive_prefix := "save"

View File

@ -21,10 +21,14 @@ class_name AssembledArchive extends Resource
# created time # created time
@export var created_time := "2024-12-24 00:00:00" @export var created_time := "2024-12-24 00:00:00"
# 全局参数
@export var global_data_dict := {}
# 不同场景的地面物品状态存档 # 不同场景的地面物品状态存档
@export var ground_archives = {} @export var ground_archives = {}
# true 为匿名false 非匿名 # true 为匿名false 非匿名
@export var npc_anonymous_states = {} @export var npc_anonymous_states = {}
# 玩家跑步锁定状态
@export var player_running_locked := true
# prop hud 显示道具 # prop hud 显示道具
@export var prop_inventory: PropInventory @export var prop_inventory: PropInventory
@ -51,3 +55,9 @@ func ground_archive(scene_name := current_scene) -> GroundArchive:
return ground_archives[scene_name] return ground_archives[scene_name]
func set_global_entry(property: StringName, value) -> void:
global_data_dict[property] = value
func get_global_value(property: StringName) -> Variant:
return global_data_dict.get(property)

View File

@ -11,7 +11,8 @@ const CANVAS_LAYER_DIALOG = 23
const CANVAS_LAYER_UI = 22 const CANVAS_LAYER_UI = 22
const CANVAS_LAYER_PROP_INSPECTOR = 21 const CANVAS_LAYER_PROP_INSPECTOR = 21
const CANVAS_LAYER_SETTINGS = 20 const CANVAS_LAYER_SETTINGS = 20
const CANVAS_LAYER_BAG = 11 const CANVAS_LAYER_BAG = 12
const CANVAS_LAYER_GROUND_MASK = 11
const CANVAS_LAYER_SHADING = 10 const CANVAS_LAYER_SHADING = 10
const CANVAS_LAYER_FG = 2 const CANVAS_LAYER_FG = 2
const CANVAS_LAYER_HD_ENTITY = 1 const CANVAS_LAYER_HD_ENTITY = 1

View File

@ -1,9 +1,7 @@
@tool @tool
extends Node extends Node
static var config := ( static var config: GlobalConfig:
load("user://data/config" + GlobalConfig.RES_FILE_FORMAT) as GlobalConfig
):
set = _set_config set = _set_config
var timer = Timer.new() var timer = Timer.new()

View File

@ -72,7 +72,7 @@ func _on_interacted() -> void:
# 传送queue free 导致 sfx 无法播放,使用全局声源 # 传送queue free 导致 sfx 无法播放,使用全局声源
sfx.global_play() sfx.global_play()
if GlobalConfig.DEBUG: if GlobalConfig.DEBUG:
print("传送前往", target_scene, target_portal) print("传送前往", target_scene, target_portal, " immediately=", immediately)
var ground_loader = SceneManager.get_ground_loader() as GroundLoader var ground_loader = SceneManager.get_ground_loader() as GroundLoader
if ground_loader: if ground_loader:
ground_loader.transition_to_scene(target_scene, target_portal, immediately) ground_loader.transition_to_scene(target_scene, target_portal, immediately)

View File

@ -1,6 +1,7 @@
@tool @tool
class_name Ground2D extends Node2D class_name Ground2D extends Node2D
@export var scene_name := ""
@export_group("Player", "player_") @export_group("Player", "player_")
@export var player_y_fixed := true @export var player_y_fixed := true
@export var player_y := 70: @export var player_y := 70:
@ -45,6 +46,11 @@ const FOOTSTEP_AUDIO = {
func _ready() -> void: func _ready() -> void:
foreground.layer = GlobalConfig.CANVAS_LAYER_FG foreground.layer = GlobalConfig.CANVAS_LAYER_FG
_reset_player_positon() _reset_player_positon()
# 检查 scene_name 是否合法
scene_name = scene_name.strip_edges()
if not scene_name or scene_name.length() != 7:
printerr("scene_name is not valid")
return
if Engine.is_editor_hint(): if Engine.is_editor_hint():
return return
# 如果 debug 模式下不通过 GroundLoader 启动,读取 palyer 位置 # 如果 debug 模式下不通过 GroundLoader 启动,读取 palyer 位置

View File

@ -8,35 +8,36 @@ class_name GroundLoader extends Node2D
@export var debug_reload := false: @export var debug_reload := false:
set(new_val): set(new_val):
debug_reload = false debug_reload = false
if current_scene and entrance_portal: if is_node_ready() and current_scene and entrance_portal:
transition_to_scene(current_scene, entrance_portal, true) transition_to_scene(current_scene, entrance_portal, true)
@export var archive_scene := "" @export var archive_scene := ""
@export var archive_portal := "" @export var archive_portal := ""
@onready var mask_layer := %MaskLayer as CanvasLayer
@onready var mask := %Mask as ColorRect
var first_entered := true var first_entered := true
var ground: Ground2D var ground: Ground2D
var display_mask_time = 0.0
var scenes_dir = "res://scene/ground/scene/" var scenes_dir = "res://scene/ground/scene/"
var ground_dict = {} # 场景名字映射到路径
var ground_scene_path_dict = {}
# 预加载 portal 通往的场景
var neighbor_scene_cache = {}
func _ready() -> void: func _ready() -> void:
mask_layer.layer = GlobalConfig.CANVAS_LAYER_GROUND_MASK
mask.visible = true
mask.color.a = 0.0
# grounds
_read_grounds() _read_grounds()
ground = get_node_or_null("Ground") # ground = get_node_or_null("Ground")
# load save # load save
if not ignore_archive: if not ignore_archive:
_load_save() _load_save()
if archive_scene and archive_portal:
current_scene = archive_scene
entrance_portal = archive_portal
if current_scene and entrance_portal: if current_scene and entrance_portal:
transition_to_scene(current_scene, entrance_portal, true) transition_to_scene(current_scene, entrance_portal, true)
elif ground:
ground.queue_free()
ground = null
func _read_grounds() -> void: func _read_grounds() -> void:
@ -47,9 +48,9 @@ func _read_grounds() -> void:
for s_file in DirAccess.open(c_path).get_files(): for s_file in DirAccess.open(c_path).get_files():
if s_file.ends_with(".tscn"): if s_file.ends_with(".tscn"):
var s_path = c_path + s_file var s_path = c_path + s_file
ground_dict[c_dir.substr(0, 3) + "_" + s_file.substr(0, 3)] = s_path ground_scene_path_dict[c_dir.substr(0, 3) + "_" + s_file.substr(0, 3)] = s_path
# # 确保每个 ground 都初始化 archive # # 确保每个 ground 都初始化 archive
# for key in ground_dict.keys(): # for key in ground_scene_path_dict.keys():
# if GlobalConfig.DEBUG: # if GlobalConfig.DEBUG:
# print("check ground_archive:", key) # print("check ground_archive:", key)
# ArchiveManager.archive.ground_archive(key) # ArchiveManager.archive.ground_archive(key)
@ -61,35 +62,41 @@ func _load_save():
archive_scene = ArchiveManager.archive.current_scene archive_scene = ArchiveManager.archive.current_scene
if ArchiveManager.archive.entrance_portal: if ArchiveManager.archive.entrance_portal:
archive_portal = 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 transition_to_scene(key: String, portal: String, immediately := false) -> void: func _toggle_mask(display: bool, _immediately: bool) -> Tween:
var scene_path = ground_dict[key] 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: if scene_path:
var scene = load(scene_path).instantiate() current_scene = scene_name
current_scene = key
entrance_portal = portal entrance_portal = portal
# 优先更新 archive使 ground 可以访问自己的 current_scene 键值 # 优先更新 archive使 ground 可以访问自己的 current_scene 键值
_update_archive() _update_archive()
if immediately: # 转场效果,在 _load_ground_node 之前播放
_do_transition(scene) var tween = _toggle_mask(true, immediately)
# 更新玩家位置 tween.tween_callback(_do_transition.bind(scene_name))
if first_entered: tween.tween_callback(_toggle_mask.bind(false, immediately))
_update_player_position()
else:
var tween = create_tween() as Tween
var player = SceneManager.get_player() as MainPlayer
if player:
player.action_locked = true
#TODO 转场效果
#
tween.tween_interval(0.2)
tween.tween_callback(_do_transition.bind(scene))
if player:
tween.tween_callback(func(): player.action_locked = false)
first_entered = false
else: else:
print("Scene not found: " + key) print("Scene not found: " + scene_name)
func _update_player_position(): func _update_player_position():
@ -104,17 +111,30 @@ func _update_player_position():
player.set_facing_direction(ArchiveManager.archive.player_direction) player.set_facing_direction(ArchiveManager.archive.player_direction)
func _do_transition(scene: Node2D): 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: if ground:
# 提前移除,防止命名冲突 # 提前移除,防止命名冲突
remove_child(ground) remove_child(ground)
ground.queue_free() # 不需要释放,因为会缓存,在 ground_node_cache 中释放
ground = scene.get_child(0) # ground.queue_free()
scene.remove_child(ground) # 先设置 ground再添加到场景中
ground.owner = null # 因为 ground 在 enter_tree 时会用到 SceneManager 的方法
scene.queue_free() # 其中间接用到了 GroundLoader 的 ground
add_child(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" ground.name = "Ground"
add_child(ground)
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():
var portal_node = ground.get_node_or_null("DeployLayer/portal_" + entrance_portal) as Node2D var portal_node = ground.get_node_or_null("DeployLayer/portal_" + entrance_portal) as Node2D
if portal_node: if portal_node:
@ -126,8 +146,11 @@ func _do_transition(scene: Node2D):
print("move player to portal:", entrance_portal, portal_node.global_position) print("move player to portal:", entrance_portal, portal_node.global_position)
else: else:
printerr(current_scene + " portal not found: " + entrance_portal) printerr(current_scene + " portal not found: " + entrance_portal)
if GlobalConfig.DEBUG and not Engine.is_editor_hint(): # 更新玩家位置
_watch_scene_update() if first_entered and not Engine.is_editor_hint():
_update_player_position()
first_entered = false
# SceneManager.release_player()
func _update_archive(): func _update_archive():
@ -138,13 +161,47 @@ func _update_archive():
archive_portal = entrance_portal 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 update_watcher: Timer
var last_modify_time = 0 var last_modify_time = 0
# DEBUG 时重新加载资源 # DEBUG 时重新加载资源
func _watch_scene_update(): func _watch_scene_update():
var scene_path = ground_dict[current_scene] var scene_path = ground_scene_path_dict[current_scene]
if scene_path: if scene_path:
last_modify_time = FileAccess.get_modified_time(scene_path) last_modify_time = FileAccess.get_modified_time(scene_path)
if not update_watcher: if not update_watcher:

View File

@ -4,3 +4,16 @@
[node name="GroundLoader" type="Node2D"] [node name="GroundLoader" type="Node2D"]
script = ExtResource("1_6mjre") script = ExtResource("1_6mjre")
[node name="MaskLayer" type="CanvasLayer" parent="."]
unique_name_in_owner = true
[node name="Mask" type="ColorRect" parent="MaskLayer"]
unique_name_in_owner = true
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
color = Color(0, 0, 0, 0)

View File

@ -73,6 +73,17 @@ func set_data(property: StringName, value: Variant) -> bool:
return false return false
func set_global_entry(property: StringName, value: Variant) -> void:
ArchiveManager.archive.set_global_entry(property, value)
func get_global_value(property: StringName, default_value = null) -> Variant:
var val = ArchiveManager.archive.get_global_value(property)
if val == null:
return default_value
return val
func _get(property: StringName) -> Variant: func _get(property: StringName) -> Variant:
if property == "oneshot_animation": if property == "oneshot_animation":
return oneshot_animation return oneshot_animation

View File

@ -15,6 +15,8 @@ func _default_data() -> Dictionary:
func _ready() -> void: func _ready() -> void:
super._ready() super._ready()
if Engine.is_editor_hint():
return
func play_intro_dialogue(): func play_intro_dialogue():
@ -42,6 +44,7 @@ func _on_deploy_layer_ready() -> void:
if paper.interacted_times > 0: if paper.interacted_times > 0:
paper.visible = false paper.visible = false
paper.enabled = false paper.enabled = false
right_door.enabled = true
else: else:
paper.visible = true paper.visible = true
paper.enabled = true paper.enabled = true

View File

@ -206,6 +206,30 @@ tracks/14/keys = {
"update": 0, "update": 0,
"values": [Vector2(1, 1)] "values": [Vector2(1, 1)]
} }
tracks/15/type = "value"
tracks/15/imported = false
tracks/15/enabled = true
tracks/15/path = NodePath("DeployLayer/oneshot纸片/Sign:display_sign")
tracks/15/interp = 1
tracks/15/loop_wrap = true
tracks/15/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
tracks/16/type = "value"
tracks/16/imported = false
tracks/16/enabled = true
tracks/16/path = NodePath("DeployLayer/oneshot纸片/Sign:position")
tracks/16/interp = 1
tracks/16/loop_wrap = true
tracks/16/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(3, -4)]
}
[sub_resource type="Animation" id="Animation_7k2c8"] [sub_resource type="Animation" id="Animation_7k2c8"]
resource_name = "intro" resource_name = "intro"
@ -546,6 +570,7 @@ metadata/_edit_horizontal_guides_ = [88.0, 122.0]
[node name="Ground" parent="." instance=ExtResource("1_ff4yb")] [node name="Ground" parent="." instance=ExtResource("1_ff4yb")]
position = Vector2(1, 0) position = Vector2(1, 0)
scene_name = "c01_s05"
[node name="AnimationPlayer" parent="Ground" index="0"] [node name="AnimationPlayer" parent="Ground" index="0"]
libraries = { libraries = {
@ -566,6 +591,7 @@ position = Vector2(27, 3)
position = Vector2(503, 11) position = Vector2(503, 11)
texture = ExtResource("4_gdhoy") texture = ExtResource("4_gdhoy")
enabled = false enabled = false
immediately = false
target_scene = "c01_s06" target_scene = "c01_s06"
target_portal = "left" target_portal = "left"
default_texture = ExtResource("4_gdhoy") default_texture = ExtResource("4_gdhoy")
@ -644,11 +670,10 @@ file = "物品查看.mp3"
[node name="Sign" parent="Ground/DeployLayer/oneshot纸片" index="2"] [node name="Sign" parent="Ground/DeployLayer/oneshot纸片" index="2"]
modulate = Color(1, 1, 1, 0) modulate = Color(1, 1, 1, 0)
offset_left = 35.0 offset_left = 3.0
offset_top = -70.0 offset_top = -4.0
offset_right = 35.0 offset_right = 3.0
offset_bottom = -70.0 offset_bottom = -4.0
display_sign = false
[node name="CollisionShape2D" parent="Ground/DeployLayer/oneshot纸片/Area2D" index="0"] [node name="CollisionShape2D" parent="Ground/DeployLayer/oneshot纸片/Area2D" index="0"]
shape = SubResource("RectangleShape2D_5s1ih") shape = SubResource("RectangleShape2D_5s1ih")

View File

@ -9,6 +9,8 @@ func _default_data() -> Dictionary:
func _ready() -> void: func _ready() -> void:
super._ready() super._ready()
if Engine.is_editor_hint():
return
func _on_deploy_layer_ready() -> void: func _on_deploy_layer_ready() -> void:

View File

@ -14,6 +14,7 @@
metadata/_edit_horizontal_guides_ = [158.0, 88.0] metadata/_edit_horizontal_guides_ = [158.0, 88.0]
[node name="Ground" parent="." instance=ExtResource("1_bitx7")] [node name="Ground" parent="." instance=ExtResource("1_bitx7")]
scene_name = "c01_s06"
[node name="AnimationPlayer" parent="Ground" index="0"] [node name="AnimationPlayer" parent="Ground" index="0"]
script = ExtResource("2_fkfhi") script = ExtResource("2_fkfhi")
@ -30,11 +31,13 @@ centered = false
[node name="portal_left" parent="Ground/DeployLayer" index="0"] [node name="portal_left" parent="Ground/DeployLayer" index="0"]
position = Vector2(144, 20) position = Vector2(144, 20)
immediately = false
target_scene = "c01_s05" target_scene = "c01_s05"
target_portal = "right" target_portal = "right"
[node name="portal_right" parent="Ground/DeployLayer" index="1"] [node name="portal_right" parent="Ground/DeployLayer" index="1"]
position = Vector2(1886, 28) position = Vector2(1886, 28)
immediately = false
[node name="男孩" type="AnimatedSprite2D" parent="Ground/DeployLayer" index="2"] [node name="男孩" type="AnimatedSprite2D" parent="Ground/DeployLayer" index="2"]
position = Vector2(547.5, 0.5) position = Vector2(547.5, 0.5)

View File

@ -9,6 +9,7 @@
[node name="S01" type="Node2D"] [node name="S01" type="Node2D"]
[node name="Ground" parent="." instance=ExtResource("1_gdcov")] [node name="Ground" parent="." instance=ExtResource("1_gdcov")]
scene_name = "c02_s01"
[node name="AnimationPlayer" parent="Ground" index="0"] [node name="AnimationPlayer" parent="Ground" index="0"]
script = ExtResource("2_uuwn3") script = ExtResource("2_uuwn3")
@ -38,7 +39,7 @@ dialogue = "c02"
position = Vector2(135, 56) position = Vector2(135, 56)
[node name="MainPlayer" parent="Ground" index="5"] [node name="MainPlayer" parent="Ground" index="5"]
position = Vector2(78, 40) position = Vector2(78, 88)
[node name="FGSprite2D" parent="Ground/ParallaxForeground/FGParallaxLayer" index="0"] [node name="FGSprite2D" parent="Ground/ParallaxForeground/FGParallaxLayer" index="0"]
texture = null texture = null

View File

@ -11,6 +11,8 @@ func _default_data() -> Dictionary:
func _ready() -> void: func _ready() -> void:
super._ready() super._ready()
if Engine.is_editor_hint():
return
func _on_deploy_layer_ready() -> void: func _on_deploy_layer_ready() -> void:

View File

@ -43,6 +43,7 @@ size = Vector2(35, 70)
[node name="S02" type="Node2D"] [node name="S02" type="Node2D"]
[node name="Ground" parent="." instance=ExtResource("1_wrr6r")] [node name="Ground" parent="." instance=ExtResource("1_wrr6r")]
scene_name = "c02_s02"
[node name="AnimationPlayer" parent="Ground" index="0"] [node name="AnimationPlayer" parent="Ground" index="0"]
libraries = { libraries = {

View File

@ -9,6 +9,8 @@ func _default_data() -> Dictionary:
func _ready() -> void: func _ready() -> void:
super._ready() super._ready()
if Engine.is_editor_hint():
return
func _on_deploy_layer_ready() -> void: func _on_deploy_layer_ready() -> void:

View File

@ -23,6 +23,7 @@ size = Vector2(40, 70)
[node name="S03" type="Node2D"] [node name="S03" type="Node2D"]
[node name="Ground" parent="." instance=ExtResource("1_lheeb")] [node name="Ground" parent="." instance=ExtResource("1_lheeb")]
scene_name = "c02_s03"
[node name="AnimationPlayer" parent="Ground" index="0"] [node name="AnimationPlayer" parent="Ground" index="0"]
script = ExtResource("2_l2oec") script = ExtResource("2_l2oec")
@ -41,7 +42,7 @@ position = Vector2(629, 2)
[node name="Npc" parent="Ground/DeployLayer" index="2" instance=ExtResource("2_r5smg")] [node name="Npc" parent="Ground/DeployLayer" index="2" instance=ExtResource("2_r5smg")]
position = Vector2(465, 23) position = Vector2(465, 23)
frame_progress = 0.799802 frame_progress = 0.514003
character_name = "张胖子" character_name = "张胖子"
dialogue_title = "张胖子_01" dialogue_title = "张胖子_01"
@ -72,7 +73,7 @@ ambient_light_energy = 2.0
position = Vector2(1120, 5) position = Vector2(1120, 5)
[node name="MainPlayer" parent="Ground" index="5"] [node name="MainPlayer" parent="Ground" index="5"]
position = Vector2(25, 40) position = Vector2(25, 88)
[node name="BGParallaxLayer" parent="Ground/ParallaxForeground" index="0"] [node name="BGParallaxLayer" parent="Ground/ParallaxForeground" index="0"]
use_parent_material = true use_parent_material = true

View File

@ -9,6 +9,8 @@ func _default_data() -> Dictionary:
func _ready() -> void: func _ready() -> void:
super._ready() super._ready()
if Engine.is_editor_hint():
return
func _on_deploy_layer_ready() -> void: func _on_deploy_layer_ready() -> void:

View File

@ -62,6 +62,17 @@ func _ready() -> void:
footstep_timer.timeout.connect(_on_footstep_timer_timeout) footstep_timer.timeout.connect(_on_footstep_timer_timeout)
footstep_timer.stop() footstep_timer.stop()
# SceneManager.focus_player(self) # SceneManager.focus_player(self)
_check_character_status()
func _enter_tree() -> void:
if is_node_ready():
_check_character_status()
func _check_character_status():
# 检查角色锁定状态
running_locked = ArchiveManager.archive.player_running_locked
func _on_footstep_timer_timeout(): func _on_footstep_timer_timeout():

View File

@ -1,8 +1,8 @@
[gd_scene load_steps=5 format=3 uid="uid://cekhj65axie0p"] [gd_scene load_steps=5 format=3 uid="uid://cekhj65axie0p"]
[ext_resource type="Script" path="res://scene/prop/prop_inspector.gd" id="1_2wpwe"] [ext_resource type="Script" path="res://scene/prop/prop_inspector.gd" id="1_2wpwe"]
[ext_resource type="Texture2D" uid="uid://f186lvt5y2ql" path="res://asset/art/ui/prop/遮罩.png" id="2_j83lq"]
[ext_resource type="Texture2D" uid="uid://cvgw2mxrlr6io" path="res://asset/art/scene/c02/s02_走道/ux_进门鼠疫海报yz.png" id="2_wr575"] [ext_resource type="Texture2D" uid="uid://cvgw2mxrlr6io" path="res://asset/art/scene/c02/s02_走道/ux_进门鼠疫海报yz.png" id="2_wr575"]
[ext_resource type="Texture2D" uid="uid://cgghff16powfg" path="res://asset/art/ui/prop遮罩.png" id="3_uf004"]
[sub_resource type="LabelSettings" id="LabelSettings_5qe7a"] [sub_resource type="LabelSettings" id="LabelSettings_5qe7a"]
line_spacing = 1.0 line_spacing = 1.0
@ -25,7 +25,7 @@ grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
mouse_filter = 2 mouse_filter = 2
texture = ExtResource("3_uf004") texture = ExtResource("2_j83lq")
[node name="CenterContainer" type="CenterContainer" parent="."] [node name="CenterContainer" type="CenterContainer" parent="."]
anchors_preset = 8 anchors_preset = 8
@ -108,6 +108,6 @@ unique_name_in_owner = true
modulate = Color(1, 1, 1, 0) modulate = Color(1, 1, 1, 0)
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 4 size_flags_horizontal = 4
text = "Q: 退出 E: 阅读" text = "Q: ui_退出 E: ui_阅读"
horizontal_alignment = 1 horizontal_alignment = 1
vertical_alignment = 1 vertical_alignment = 1

49
util/lru.gd Normal file
View File

@ -0,0 +1,49 @@
@tool
class_name LRU extends RefCounted
# LRU 算法
var cache_size = 1
# keys. 新的在后面,旧的在前面
var lru_list = []
# key -> value
var cache = {}
var cache_mutex = Mutex.new()
func _init(size := 8) -> void:
cache_size = size
func _get(key: Variant) -> Variant:
var v = null
cache_mutex.lock()
if cache.has(key):
lru_list.erase(key)
lru_list.append(key)
v = cache[key]
cache_mutex.unlock()
return v
func _set(key: StringName, value: Variant) -> bool:
cache_mutex.lock()
if cache.has(key):
lru_list.erase(key)
if cache[key] != value:
_release_value(cache[key])
cache[key] = value
lru_list.append(key)
if lru_list.size() > cache_size:
var k = lru_list.pop_front()
_release_value(cache[k])
cache.erase(k)
cache_mutex.unlock()
return true
func _release_value(v: Variant) -> void:
if v is Node:
if not v.is_inside_tree() and is_instance_valid(v):
v.queue_free()
elif not v is RefCounted:
v.free()