demo 0.1 细节优化

This commit is contained in:
cakipaul 2025-06-30 18:33:40 +08:00
parent 3ede841004
commit f0666a5072
94 changed files with 388 additions and 319 deletions

View File

@ -127,7 +127,7 @@ func _on_animation_finished() -> void:
var next = auto_checkout_dict[intro].animation_next
if wait_time > 0:
pause()
get_tree().create_timer(wait_time).timeout.connect(play.bind(next))
Util.timer(wait_time, play.bind(next))
else:
play(next)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
asset/art/prop/c02/海报特写/青岛啤酒.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b0bkwqv86sc0"
path="res://.godot/imported/青岛啤酒(未用).png-80f5ab64f6d0a693fa9d7a06bf9521c2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://asset/art/prop/c02/海报特写/青岛啤酒(未用).png"
dest_files=["res://.godot/imported/青岛啤酒(未用).png-80f5ab64f6d0a693fa9d7a06bf9521c2.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

@ -43,7 +43,7 @@ c01_7老板_7,手脚麻利点,不要耽误店里的生意。,书店老板,,,
c01_8监督小孩_1,发赏钱喽!发赏钱喽!,监督小孩,,,,
c01_8监督小孩_2,哟,吕萍?你咋表演结束了才回来...,监督小孩,,,,
c01_8监督小孩_3,你手里攥的什么东西?给我看看,不然我就找班主告状!,监督小孩,,,,
c01_8监督小孩_4,这上头咋还刻着个人哩,脑袋圆圆的,一根毛都没有!,监督小孩,,[#wait=3.0],,
c01_8监督小孩_4,这上头咋还刻着个人哩,脑袋圆圆的,一根毛都没有!,监督小孩,,[#wait=1.0],,
c01_8监督小孩_5,吕萍,你从哪弄来的?,监督小孩,,,,
c01_8小小蝶_1,你们...都看见了吗?,吕萍,,,,
c01_8幼年陆仁_1,它要来了,快跑!,陆仁,,,,

1 keys zh_CN _character _notes _tags zh_SH en
43 c01_8监督小孩_1 发赏钱喽!发赏钱喽! 监督小孩
44 c01_8监督小孩_2 哟,吕萍?你咋表演结束了才回来... 监督小孩
45 c01_8监督小孩_3 你手里攥的什么东西?给我看看,不然我就找班主告状! 监督小孩
46 c01_8监督小孩_4 这上头咋还刻着个人哩,脑袋圆圆的,一根毛都没有! 监督小孩 [#wait=3.0] [#wait=1.0]
47 c01_8监督小孩_5 吕萍,你从哪弄来的? 监督小孩
48 c01_8小小蝶_1 你们...都看见了吗? 吕萍
49 c01_8幼年陆仁_1 它要来了,快跑! 陆仁

View File

@ -85,7 +85,7 @@
~ c01_s12_鬼差来之前对话
监督小孩: 这上头咋还刻着个人哩,脑袋圆圆的,一根毛都没有! [#wait=3.0] [ID:c01_8监督小孩_4]
监督小孩: 这上头咋还刻着个人哩,脑袋圆圆的,一根毛都没有! [#wait=1.0] [ID:c01_8监督小孩_4]
监督小孩: 吕萍,你从哪弄来的? [ID:c01_8监督小孩_5]
=> END

View File

@ -19,11 +19,12 @@ setting_日志,笔记B,,,,,Open the Log
setting_记忆,记忆G,,,,,
setting_全屏游戏,全屏游戏,,,,,Full Screen
setting_窗口置顶,窗口置顶,,,,,Top Window
ux_panel_笔记,线索,,,,,
index_新游戏,新游戏,,,,,New Game
index_继续游戏,继续游戏,,,,,Resume
index_退出游戏,退出游戏,,,,,Exit
ux_panel_继续,继续,,,,,
ux_panel_笔记,笔记,,,,,
ux_panel_线索笔记,线索,,,,,
ux_panel_设置,设置,,,,,
ux_panel_返回主菜单,返回主菜单,,,,,
ux_panel_退出游戏,退出游戏,,,,,

1 keys zh_CN _character _notes _tags zh_SH en
19 setting_记忆 记忆(G)
20 setting_全屏游戏 全屏游戏 Full Screen
21 setting_窗口置顶 窗口置顶 Top Window
22 ux_panel_笔记 线索
23 index_新游戏 新游戏 New Game
24 index_继续游戏 继续游戏 Resume
25 index_退出游戏 退出游戏 Exit
26 ux_panel_继续 继续
27 ux_panel_笔记 ux_panel_线索笔记 笔记 线索
28 ux_panel_设置 设置
29 ux_panel_返回主菜单 返回主菜单
30 ux_panel_退出游戏 退出游戏

View File

@ -5,7 +5,7 @@
退出游戏[ID:index_退出游戏]
继续 [ID:ux_panel_继续]
笔记 [ID:ux_panel_笔记]
线索 [ID:ux_panel_线索笔记]
设置 [ID:ux_panel_设置]
返回主菜单 [ID:ux_panel_返回主菜单]
退出游戏 [ID:ux_panel_退出游戏]

View File

@ -25,7 +25,7 @@ func c02_cat_play_with_door():
var knock_stream = preload("uid://6q5qi1qon35r")
AudioManager.play_sfx(knock_stream)
SceneManager.lock_player(1.5, 6, true)
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
knocking = false
@ -59,14 +59,14 @@ func _on_c02_fire_count_down_timeout():
c02_fire_count_down_timer.stop()
AudioManager.stop_bgm_music("霸凌救小蝉倒计时")
DialogueManager.show_dialogue_balloon(dialogue_c02, "c02_未完成拯救小蝉的游戏", [GlobalConfig.DIALOG_IGNORE_INPUT])
await get_tree().create_timer(1.0).timeout
await Util.wait(1.0)
SceneManager.show_black_hand(true, 0.5)
await get_tree().create_timer(3.0).timeout
await Util.wait(3)
SceneManager.unlock_player()
SceneManager.get_ground_loader().transition_to_scene("c02_s03", "4")
# TODO 音效
# res://asset/audio/BGM/心跳背景音.mp3
await get_tree().create_timer(3.0).timeout
await Util.wait(3)
SceneManager.pop_debug_dialog_info("音效", "霸凌救小蝉倒计时")
AudioManager.loop_bgm_music("霸凌救小蝉倒计时", c02_fire_count_down_sfx, 5)
c02_fire_count_down_timer.start()

View File

@ -9,7 +9,7 @@ enum VIBE {
# 从 ground loader 控制该信号
signal ground_ready(ground: Ground2D)
signal ground_start(ground: Ground2D)
signal ground_start()
func _ready():
@ -25,6 +25,12 @@ func get_ground_loader() -> GroundLoader:
return get_node_or_null("/root/Main/GroundLoader") as GroundLoader
func toggle_ground_mask(
display: bool, wait_time := 1.5, ease_min_duration := 0.5, mask_color := Color.BLACK
) -> Tween:
return get_ground_loader().toggle_mask(display, wait_time, ease_min_duration, mask_color)
# restart scene in debug launch...
func is_restarting() -> bool:
var ground = get_ground()
@ -233,7 +239,7 @@ func pop_os_with_str(translation_key: String, auto_lock := true, auto_unlock :=
var player = get_player() as MainPlayer
if player:
var msg = tr(translation_key).replace("<br>", "\n")
var lines = await DialogueUtil.generate_lines(msg)
var lines = await Util.generate_lines(msg)
player.pop_os(lines, auto_lock, auto_unlock)
else:
printerr("Player node not found")
@ -293,12 +299,9 @@ func checkout_index_page(transition := true):
# 保存后,渐隐切换场景
ArchiveManager.save_all()
if transition:
var ground = get_ground()
if ground:
var tween = create_tween()
if GlobalConfig.DEBUG:
print("transition to index page")
tween.tween_property(ground, "modulate:a", 0.0, 1.0)
var ground_loader = get_ground_loader() as GroundLoader
if ground_loader:
var tween = toggle_ground_mask(true)
tween.tween_callback(_jump_back_to_index_and_quit_main)
else:
_jump_back_to_index_and_quit_main()
@ -387,12 +390,11 @@ func toggle_pause_counter(plus := true):
func quit_game():
ArchiveManager.save_all()
var ground = get_ground()
if ground:
var tween = create_tween()
var ground_loader = get_ground_loader() as GroundLoader
if ground_loader:
var tween = toggle_ground_mask(true, 2.0, 1.0)
if GlobalConfig.DEBUG:
print("quit_game with transition")
tween.tween_property(ground, "modulate:a", 0.0, 1.5)
tween.tween_callback(get_tree().quit)
else:
get_tree().quit()

View File

@ -43,6 +43,7 @@ buses/default_bus_layout="res://config/default_bus_layout.tres"
[autoload]
Util="*res://util/util.gd"
DebugMenu="*res://addons/debug_menu/debug_menu.tscn"
GlobalConfigManager="*res://manager/config_manager/global_config_manager.gd"
ArchiveManager="*res://manager/archive_manager/archive_manager.gd"

View File

@ -424,8 +424,7 @@ func _os_load_line(line, duration):
os_tween.pause()
if os_pausing_timer and os_pausing_timer.timeout.is_connected(os_tween.play):
os_pausing_timer.timeout.disconnect(os_tween.play)
os_pausing_timer = get_tree().create_timer(duration)
os_pausing_timer.timeout.connect(_on_os_line_timeout, CONNECT_ONE_SHOT)
Util.timer(duration, _on_os_line_timeout)
func _on_os_line_timeout(naturally := true):

View File

@ -44,7 +44,7 @@ func _exit_tree() -> void:
func _create_timer(duration: float, callable: Callable):
if duration > 0 and callable:
get_tree().create_timer(duration).timeout.connect(callable)
Util.timer(duration, callable)
# Freeze 相关方法

View File

@ -243,23 +243,11 @@ func apply_dialogue_line() -> void:
# eg. [#wait=2.5]
if wait:
wait_time = wait.to_float()
await get_tree().create_timer(wait_time).timeout
await Util.wait(wait_time)
# 在 translation key 仍旧是当前 line 时跳转;如果不再是当前 line则不跳转
if dialogue_line.translation_key == initial_translation_key:
next(dialogue_line.next_id)
# var time = next_dialogue_line.text.length() * 0.2 if next_dialogue_line.time == "auto" else next_dialogue_line.time.to_float()
# await get_tree().create_timer(time).timeout
# elif dialogue_line.time != "":
# var time = dialogue_line.text.length() * 0.02 if dialogue_line.time == "auto" else dialogue_line.time.to_float()
# await get_tree().create_timer(time).timeout
# next(dialogue_line.next_id)
# else:
# is_waiting_for_input = true
# balloon.focus_mode = Control.FOCUS_ALL
# balloon.grab_focus()
## Go to the next line
func next(next_id: String) -> void:

View File

@ -148,7 +148,7 @@ func apply_dialogue_line() -> void:
is_waiting_for_input = true
# balloon.focus_mode = Control.FOCUS_ALL
# balloon.grab_focus()
await get_tree().create_timer(2.0).timeout
await Util.wait(2.0)
# debug line 不需要下一行,直接释放(避免触发 dialogue_ended 信号)
queue_free()

View File

@ -210,11 +210,11 @@ func flash_palette(duration := 0.5) -> void:
if not is_node_ready():
return
add_mode("palette")
get_tree().create_timer(duration).timeout.connect(erase_mode.bind("palette"))
Util.timer(duration, erase_mode.bind("palette"))
# glitch 闪烁
func flash_glitch(duration := 1.0) -> void:
if not is_node_ready():
return
add_mode("glitch")
get_tree().create_timer(duration).timeout.connect(erase_mode.bind("glitch"))
Util.timer(duration, erase_mode.bind("glitch"))

View File

@ -17,5 +17,5 @@ func run_effect(play_sfx := true, queue_free_delay := 0.0):
func _on_animation_finished(_a, queue_free_delay):
if queue_free_delay > 0:
await get_tree().create_timer(queue_free_delay).timeout
await Util.wait(queue_free_delay)
queue_free()

View File

@ -55,6 +55,31 @@ var dialogue_res = preload("res://asset/dialogue/npc.dialogue")
var base_scale := Vector2.ONE
var base_mod := Color.WHITE_SMOKE
var speaking_sign_tween: Tween
# 0 hide; 1 silent; 2 speaking
var speaking_sign_mode := 0:
set(val):
if speaking_sign_mode != val:
speaking_sign_mode = val
if speaking_sign_tween and speaking_sign_tween.is_valid():
speaking_sign_tween.kill()
speaking_sign_tween = create_tween()
if val == 0:
speaking_sign_tween.tween_property(speaking_sign, "modulate:a", 0.0, 0.3)
elif val == 1:
speaking_sign_tween.tween_property(speaking_sign, "modulate", base_mod, 0.3)
speaking_sign_tween.parallel().tween_property(
speaking_sign, "scale", base_scale, 0.3
)
speaking_animation.play("speaking")
elif val == 2:
speaking_sign_tween.tween_property(speaking_sign, "modulate", Color.WHITE, 0.3)
speaking_sign_tween.parallel().tween_property(
speaking_sign, "scale", base_scale * 1.3, 0.3
)
speaking_animation.play("speaking")
func _ready() -> void:
# sign position
@ -69,10 +94,14 @@ func _ready() -> void:
sign_snapper.action_on_arrived = action_key
sign_snapper.radius = walk_to_edge_width
sign_snapper.enabled = snap_to_edge
# 设置 speaking_sign 默认值
base_scale = speaking_sign.scale
base_mod = speaking_sign.modulate
speaking_sign.modulate.a = 0.0
if Engine.is_editor_hint():
# editor 下都显示
speaking_sign.visible = true
speaking_sign.modulate.a = 1
speaking_sign.modulate.a = 1.0
speaking_sign.get_node("Sprite2D").position.x = -60.0
speaking_sign.get_node("Sprite2D").frame = 2
sign_mark.display_sign = true
@ -80,10 +109,6 @@ func _ready() -> void:
# setup default value
ground_archive = ArchiveManager.archive.ground_archive()
icount = ground_archive.get_value(name, "icount", 0)
# 默认为 0
speaking_sign.modulate.a = 0.0
base_scale = speaking_sign.scale
base_mod = speaking_sign.modulate
if snap_to_edge:
sign_snapper.arrived.connect(_on_interacted)
else:
@ -106,10 +131,10 @@ func _align_signs_status():
func _on_toggle_active(activated: bool) -> void:
if activated:
speaking_animation.play("speaking")
# icount 影响 visible; activated 影响 modulate
speaking_sign.modulate.a = 1 if activated else 0
if activated and speaking_sign_mode == 0:
speaking_sign_mode = 1
elif not activated and speaking_sign_mode > 0:
speaking_sign_mode = 0
func _on_interacted() -> void:
@ -122,13 +147,9 @@ func _on_interacted() -> void:
ground_archive.set_pair(name, "icount", icount)
DialogueManager.show_dialogue_balloon(dialogue_res, dialogue_title)
interacted.emit()
var tween = create_tween()
tween.tween_property(speaking_sign, "modulate", Color.WHITE, 0.5)
tween.parallel().tween_property(speaking_sign, "scale", base_scale * 1.3, 0.3)
speaking_sign_mode = 2
await DialogueManager.dialogue_ended
tween = create_tween()
tween.tween_property(speaking_sign, "modulate", base_mod, 0.5)
tween.parallel().tween_property(speaking_sign, "scale", base_scale, 0.3)
speaking_sign_mode = 1
if GlobalConfig.DEBUG:
print("[" + name + "] call lock")
SceneManager.unlock_player()

View File

@ -116,7 +116,7 @@ script = ExtResource("4_j5svs")
[node name="Sign" parent="." instance=ExtResource("4_nokx4")]
unique_name_in_owner = true
sign_mark_offset = Vector2(0, -60)
sign_mark_offset = Vector2(0, -10)
[node name="SpeakingAnimationPlayer" type="AnimationPlayer" parent="."]
unique_name_in_owner = true
@ -141,7 +141,7 @@ scale = Vector2(0.07, 0.07)
[node name="Sprite2D" type="AnimatedSprite2D" parent="SpeakingSign2D"]
light_mask = 16
position = Vector2(60, 0)
position = Vector2(-60, 0)
sprite_frames = SubResource("SpriteFrames_k7dca")
offset = Vector2(60, 0)
@ -158,5 +158,6 @@ unique_name_in_owner = true
script = ExtResource("8_7lwt5")
radius = 25.0
walk_to_edge = true
face_inside_on_edge = true
action_on_arrived = 4
metadata/_custom_type_script = "uid://cnt01hiw52bmn"

View File

@ -13,6 +13,8 @@ signal arrived
@export_range(0.0, 20.0, 0.1) var radius := 0.0
# 让玩家走到边缘,特别适用于 npc 对话等情景
@export var walk_to_edge := false
# 如果 walk_to_edge 为 true那么走到 edge 后面朝内侧
@export var face_inside_on_edge := false
@export var action_on_arrived := 3
@export_tool_button("debug 检查玩家触发位置") var debug_check_player_pos = _debug_check_player_pos
@ -65,6 +67,12 @@ func _on_interacted():
tween = create_tween()
else:
tween = player.walk_to(target_pos)
if walk_to_edge and face_inside_on_edge:
# 玩家向右走到 target_pos.x 后,向左看向 global_position.x
if player_x < target_pos.x and target_pos.x > global_position.x:
tween.tween_callback(player.set_facing_direction.bind(Vector2(-1, 0)))
elif player_x > target_pos.x and target_pos.x < global_position.x:
tween.tween_callback(player.set_facing_direction.bind(Vector2(1, 0)))
if delay_arrived > 0:
tween.tween_interval(delay_arrived)
if action_on_arrived != 3:

View File

@ -22,7 +22,6 @@ var _tweeked_position := Vector2.ZERO
var zoom_tween: Tween
var focus_offset := Vector2.ZERO
var shaked_offset := Vector2.ZERO
var shake_ignore_boundary := false
@ -31,7 +30,7 @@ func _ready() -> void:
push_error("Focusing node not found")
func shake_camera(strength := 7.0, recovery_speed := 4.0, ignore_boundary := false):
func shake_camera(strength := 6.0, recovery_speed := 2.0, ignore_boundary := true):
shake_strength = strength
shake_recovery_speed = recovery_speed
shake_ignore_boundary = ignore_boundary
@ -56,43 +55,56 @@ func tweak_position(velocity, facing_direction):
_tweeked_position.y = facing_direction.y * abs(velocity.y) * 0.2
# 处理过程的当下理想位置
var progressing_position: Vector2
func _physics_process(delta: float) -> void:
if not focusing_node:
return
# handle shake, via _shaked_position
if shake_strength > 0.0:
shake_strength = lerpf(shake_strength, 0.0, shake_recovery_speed * delta)
shaked_offset = Vector2(
randf_range(-shake_strength, shake_strength),
randf_range(-shake_strength, shake_strength)
)
else:
shaked_offset = Vector2.ZERO
shake_ignore_boundary = false
# set camera's position
var target_position = (
focusing_node.global_position
+ _tweeked_position
+ force_offset
+ focus_offset
+ shaked_offset
)
# 最终目标位置
var target_position = focusing_node.global_position + _tweeked_position + force_offset
if focusing_node is MainPlayer:
# player 的焦点在脚底,所以需要偏移 player 的高度。注意 y 轴是向下的,所以是减去 player 的高度
target_position.y -= focusing_node.current_animation_config.os_height * 0.7
# clamp the position
if not shake_ignore_boundary:
var margin = half_screen_size / zoom_ratio
margin.y += shaded_height
target_position.x = clamp(target_position.x, limit_left + margin.x, limit_right - margin.x)
target_position.y = clamp(target_position.y, limit_top + margin.y, limit_bottom - margin.y)
target_position = _clamp_boundary(target_position)
# easing with speed
global_position = lerp(global_position, target_position, speed * delta)
progressing_position = lerp(progressing_position, target_position, speed * delta)
global_position = progressing_position
# handle shake
if shake_strength > 0.0:
# 让 shake_strength 逐帧衰减
shake_strength = lerpf(shake_strength, 0.0, shake_recovery_speed * delta)
# [-shake_strength, +shake_strength] 范围内的同时,尽可能偏离中心
# 在 0 2π 之间随机一个方向,在 _last_shake_angle 的对角范围
# _last_shake_angle = wrapf(_last_shake_angle + randf_range(-2.0, 2.0), 0, TAU) # TAU = 2π
var _last_shake_angle := randf_range(-TAU, TAU) # TAU = 2π
var shaked_offset := (
Vector2(cos(_last_shake_angle), sin(_last_shake_angle)) * shake_strength
)
# var shaked_offset := Vector2(
# randf_range(-shake_strength, shake_strength),
# randf_range(-shake_strength, shake_strength)
# )
global_position += shaked_offset
if not shake_ignore_boundary:
global_position = _clamp_boundary(global_position)
# var taget_zoom = lerpf(zoom.x, zoom_ratio, speed * delta)
# zoom = Vector2(taget_zoom, taget_zoom)
zoom = Vector2(zoom_ratio, zoom_ratio)
func _clamp_boundary(target: Vector2) -> Vector2:
var margin = half_screen_size / zoom_ratio
margin.y += shaded_height
target.x = clamp(target.x, limit_left + margin.x, limit_right - margin.x)
target.y = clamp(target.y, limit_top + margin.y, limit_bottom - margin.y)
return target
func tween_zoom(ratio: float, duration := 1.5):
if zoom_tween and zoom_tween.is_running():
zoom_tween.kill()

View File

@ -3,5 +3,5 @@
[ext_resource type="Script" uid="uid://dphabatkubjgf" path="res://scene/ground/camera/camera_focus_marker.gd" id="1_7t4e6"]
[node name="CameraFocusMarker" type="Camera2D"]
process_mode = 3
process_mode = 1
script = ExtResource("1_7t4e6")

View File

@ -18,7 +18,6 @@ class_name GroundLoader extends Node2D
var has_entered := false
var ground: Ground2D
var display_mask_sec = 0.0
# 场景名字映射到路径
static var GROUND_SCENE_PATH_DICT = {
@ -52,9 +51,11 @@ static var GROUND_SCENE_PATH_DICT = {
func _ready() -> void:
mask_layer.layer = GlobalConfig.CANVAS_LAYER_GROUND_MASK
mask.visible = true
mask.color.a = 0.0
mask_layer.layer = GlobalConfig.CANVAS_LAYER_GROUND_MASK
# mask layer 独立的 always 处理模式,可以保证转场正常运行
# toggle_mask = mask_layer.toggle_mask
ground = get_node_or_null("Ground") as Ground2D
if ground:
print("GroundLoader remove old ground:", ground.scene_name)
@ -69,6 +70,35 @@ func _ready() -> void:
# transition_to_scene(current_scene, entrance_portal, 0.0)
# # var toggle_mask:Callable
# func toggle_mask(display: bool, mask_color: Color, wait_time: float) -> Tween:
# return mask_layer.toggle_mask(display, mask_color, wait_time)
var display_start_sec = 0.0
# wait_time 包含 ease in + wait + ease out 完整时长
# ease duration = min(ease_min_duration, wait_time * 0.5)
func toggle_mask(
display: bool, wait_time: float, ease_min_duration := 0.3, mask_color:= Color.BLACK
) -> Tween:
var tween = get_tree().create_tween()
mask_color.a = mask.color.a
mask.color = mask_color
var duration = min(ease_min_duration, wait_time * 0.5)
if display:
display_start_sec = Time.get_ticks_msec() * 0.001
tween.tween_property(mask, "color:a", 1.0, duration).set_trans(Tween.TRANS_CUBIC)
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_start_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 _load_save():
# 强制覆盖 archive 记录
if force_archive_scene or force_archive_portal:
@ -82,27 +112,8 @@ func _load_save():
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
scene_name: String, portal: String, wait_time := 1.4
) -> void:
if ground:
print("GroundLoader transition_to_scene: pause prev ground.")
@ -119,11 +130,11 @@ func transition_to_scene(
_update_archive()
if wait_time > 0.0:
# 转场效果,在 _load_ground_node 之前播放
var tween = toggle_mask(true, mask_color, wait_time)
var tween = toggle_mask(true, wait_time)
tween.tween_callback(_do_transition.call_deferred.bind(scene_name))
_allow_ground_start = false
# 等到 toggle_mask 结束,再重置 freeze 状态
toggle_mask(false, mask_color, wait_time).tween_callback(
toggle_mask(false, wait_time).tween_callback(
func(): _allow_ground_start = true
)
else:
@ -141,7 +152,7 @@ var _allow_ground_start := false:
if val:
if ground.process_mode != Node.PROCESS_MODE_INHERIT:
# ground_start 信号
SceneManager.ground_start.emit(ground)
SceneManager.ground_start.emit()
ground.process_mode = Node.PROCESS_MODE_INHERIT
print(
"GroundLoader _allow_ground_start: unfrozen. frozen duration(ms):",
@ -177,7 +188,7 @@ func _do_transition(scene_name: String) -> void:
_add_ground()
if _allow_ground_start:
# 如果不阻塞,直接 ground_start 信号
SceneManager.ground_start.emit(ground)
SceneManager.ground_start.emit()
# 预先加载邻居场景
_post_transition()
if GlobalConfig.DEBUG and not Engine.is_editor_hint():

View File

@ -25,6 +25,7 @@ color = Color(0, 0, 0, 1)
[node name="MaskLayer" type="CanvasLayer" parent="."]
unique_name_in_owner = true
process_mode = 3
layer = 14
[node name="Mask" type="ColorRect" parent="MaskLayer"]

View File

@ -56,10 +56,6 @@ func _on_ground_ready() -> void:
pass
func _on_ground_start() -> void:
pass
func _on_ready() -> void:
if Engine.is_editor_hint():
return

View File

@ -21,7 +21,7 @@ func intro_start():
await animation_finished
SceneManager.release_player()
$"../DeployLayer/床".enabled = true
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
show_interact_help()
func play_intro_dialogue():
@ -37,7 +37,7 @@ func _on_ground_ready() -> void:
piano = $"../DeployLayer/钢琴"
if data["oneshot_animation_played"]:
$"../DeployLayer/床".enabled = true
get_tree().create_timer(1.0).timeout.connect(show_interact_help)
Util.timer(1.0, show_interact_help)
# 画框是否已经正位
data.frame_relocated = ambush.played and ambush.one_shot
# 禁用鸡毛掸子
@ -81,15 +81,15 @@ func cant_read():
func _on_note_read():
frame.enabled = false
# 稍加延时后显示鸡毛掸子
get_tree().create_timer(4.5).timeout.connect(func(): ambush.enabled = true)
Util.timer(4.5, func(): ambush.enabled = true)
func _on_ambush_triggered():
ambush.enabled = false
frame.note_key = "c01_摆正的洋相片"
get_tree().create_timer(5.5).timeout.connect(func(): frame.enabled = true)
Util.timer(5.5, func(): frame.enabled = true)
# 鸡毛掸子 4.5s,再等待 3s 后掉落纸片
get_tree().create_timer(8).timeout.connect(_play_paper_animation)
Util.timer(8, _play_paper_animation)
func lock_on_use_stick():
SceneManager.lock_player(4.0)

View File

@ -97,12 +97,12 @@ func pre_game_intro():
camera.tween_zoom(1.5, 3.0)
var p = $"../DeployLayer/四小孩画鬼差的对话ambush/FocusPoint"
camera.focus_node(p, 3.0)
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s06_四个小孩画鬼差的对话")
await DialogueManager.dialogue_ended
# 重置镜头
SceneManager.focus_player_and_reset_zoom(2.5)
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
await SceneManager.pop_os_with_str("c01_s06_熟悉的墙画")
SceneManager.unlock_player()
@ -117,7 +117,6 @@ func game_intro() -> void:
var camera = SceneManager.get_camera_marker() as CameraFocusMarker
camera.tween_zoom(1.5, 3.0)
camera.focus_node(focus_point, 3.0)
# get_tree().create_timer(1.0).timeout.connect(camera.focus_node.bind(focus_point, 3.))
standing_kid1.play("【站立小孩-1】挠痒呼吸")
standing_kid2.play("【站立小孩-2】转身")
# 只有 1、2 是跟班3 不参与
@ -144,7 +143,7 @@ func _game_counting_down(_res = null):
var player = SceneManager.get_player() as MainPlayer
var left = player.global_position.x
player.player_movement_rect.position.x = left
get_tree().create_timer(2.0).timeout.connect(_kids_start_run)
Util.timer(2.0, _kids_start_run)
cat.visible = true
cat.play("【墙上黑猫】跑步")
cat.get_node("猫咪嘶吼音效").play()
@ -175,7 +174,7 @@ func _on_talked():
# 三个小孩都对话完毕
await DialogueManager.dialogue_ended
SceneManager.lock_player()
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
await SceneManager.pop_os_with_str("c01_s06_四小孩对话结束")
SceneManager.unlock_player()
@ -243,6 +242,7 @@ func obstacles_ambush3_triggered():
game_kid.sprite_frames.set_animation_loop("【胖小孩背着残疾小孩】摔倒", false)
game_kid.animation_finished.connect(_on_kid_fall_finished, CONNECT_ONE_SHOT)
# 残疾小孩冲刺
func _on_kid_fall_finished():
obstacles.get_node("遮盖").visible = true
@ -258,7 +258,6 @@ func _on_kid_fall_finished():
tween.tween_property(cat_shadow, "modulate:a", 0.0, 4.0)
func _on_mid_ambush_success():
obstacles_success = true
# TODO 获得成就
@ -273,11 +272,10 @@ func game_restart():
game_kid.play("【胖小孩背着残疾小孩】侧面呼吸")
SceneManager.freeze_player(0)
SceneManager.pop_debug_dialog_info("音效", "玩家被抓住,猫鼠游戏重新开始")
await get_tree().create_timer(1.5).timeout
var ground_loader = SceneManager.get_ground_loader()
await Util.wait(1.5)
# 过场黑屏
ground_loader.toggle_mask(true, Color.BLACK, 0.5)
await get_tree().create_timer(0.5).timeout
SceneManager.toggle_ground_mask(true)
await Util.wait(0.5)
# 重置桌椅
obstacles_pushed = false
obstacles_success = false
@ -288,13 +286,13 @@ func game_restart():
standing_kid1.global_position.x = kids_start_run_initial_x[0]
standing_kid2.global_position.x = kids_start_run_initial_x[1]
game_kid.global_position.x = kids_start_run_initial_x[2]
ground_loader.toggle_mask(false, Color.BLACK, 1.0)
await get_tree().create_timer(1.0).timeout
SceneManager.toggle_ground_mask(false)
await Util.wait(1.0)
# 开始跑
SceneManager.release_player()
$"Sfx猫鼠游戏".set("parameters/switch_to_clip", "Intro")
DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s06_猫鼠游戏倒计时")
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
_kids_start_run()
@ -369,7 +367,7 @@ func transport_player_to_next_scene(win: bool):
_show_next_scene(true)
SceneManager.pop_debug_dialog_info("音效", "猫鼠游戏胜利,无缝转场")
else:
get_tree().create_timer(0.7).timeout.connect(_show_next_scene.bind(false))
Util.timer(0.7, _show_next_scene.bind(false))
SceneManager.pop_debug_dialog_info("音效", "猫鼠游戏失败,传送下一场景")

View File

@ -34,7 +34,7 @@ func _on_ground_ready() -> void:
var camera = SceneManager.get_camera_marker()
camera.limit_left = -400
create_tween().tween_property(camera, "limit_left", 0, 5.0)
get_tree().create_timer(5.0).timeout.connect(_on_fog_disappear)
Util.timer(5.0, _on_fog_disappear)
seller = $"../DeployLayer/报童" as AnimatedSprite2D
ambush_seller = $"../DeployLayer/报童/Ambush报童" as Ambush2D
bookstore_portal = $"../DeployLayer/portal_2"
@ -67,9 +67,9 @@ func _on_luren_animation_finished() -> void:
func _knock_door():
# 6 号动作:敲门
SceneManager.lock_player(3.5, 6)
await get_tree().create_timer(2.2).timeout
await Util.wait(2.2)
$"敲门音效".play()
await get_tree().create_timer(1.2).timeout
await Util.wait(1.2)
var stream = preload("res://asset/audio/sfx/交互/序章/03_书店外黄昏_开门.wav")
AudioManager.play_sfx(stream)
@ -80,8 +80,6 @@ func _on_fog_disappear() -> void:
var chapter_title = preload("res://asset/art/scene/c01/s02_旧版序章/f_序章标题.png")
SceneManager.pop_center_texture(chapter_title)
fog.tween_fog(0, Color.TRANSPARENT, Fog2D.FOG_OFFSET_DEFAULT, 15.0, true)
# await get_tree().create_timer(5.0).timeout
func seller_interacted():
bookstore_portal.holding = false
@ -90,7 +88,7 @@ func seller_interacted():
seller.play("报童给报纸")
# 提前写入 prop 中,防止存档 bug稍后播放获得动画
SceneManager.enable_prop_item_silently("prop_信碎片2")
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
$"报童音效".play()
SceneManager.pop_os_with_str("c01_s07_获得报纸")
await SceneManager.get_player().os_finished

View File

@ -210,7 +210,7 @@ loop = true
metadata/_custom_type_script = "uid://wapo47a1oddf"
[node name="Npc吉祥话4" parent="Ground/DeployLayer/陆仁舞刀" instance=ExtResource("6_fw22n")]
position = Vector2(-22, 51)
position = Vector2(-14, 51)
enabled = false
speaking_sign_height = 47.0
dialogue_title = "c01_s07_陆仁吉祥话"

View File

@ -91,7 +91,7 @@ func _on_envolope_table_interacted() -> void:
func first_enter_door() -> void:
SceneManager.freeze_player(0)
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s08_书店进门老板台词")
await DialogueManager.dialogue_ended
SceneManager.release_player()
@ -100,7 +100,7 @@ func first_enter_door() -> void:
func assign_tasks() -> void:
# 放报纸动作
SceneManager.freeze_player(0, 8, false)
await get_tree().create_timer(4.5).timeout
await Util.wait(4.5)
$"放报纸音效".play()
DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s08_书店老板任务")
await DialogueManager.dialogue_ended
@ -126,7 +126,9 @@ func _on_shelf_game_exiting() -> void:
create_tween().tween_property(mask, "color:a", 0.0, 1.0).from(1.0)
SceneManager.release_player()
var fall_off
var fall_off
func _on_shelf_game_success() -> void:
ArchiveManager.archive.set_global_entry(&"c01_shelf_game_success", true)
@ -146,31 +148,35 @@ func _on_shelf_game_success() -> void:
fall_off.play()
fall_off.animation_finished.connect(_on_fall_off_finished)
# 从 sfx_生死簿演出 中退出时播放不出来,需要在此处播放
$"摔倒音效".play()
Util.timer(0.5, $"摔倒音效".play)
_check_portal()
func _on_fall_off_finished() -> void:
fall_off.visible = false
# 最后释放玩家
SceneManager.release_player()
SceneManager.get_player().hide_sprite = false
await get_tree().create_timer(1.2).timeout
await Util.wait(1.2)
SceneManager.pop_os_with_str("c01_s08_书架游戏完成")
func _setup_weird_bookstore() -> void:
# 切换背景音效
$"环境音".stop()
$"诡异环境音".play()
# 光变红
$"../AmbientLayer/PointLight2D".color = Color.html("#ff2719")
coin.enabled = true
ladder.enabled = false
manager.visible = false
mice.visible = true
$"../DirectionalLight2D".energy = 0.7
$"../BGSprite2D".texture = preload("res://asset/art/scene/c01/s08_书店/夜晚版/bg_书店夜晚.png")
$"../ParallaxForeground/FGParallaxLayer/FGSprite2D".texture = preload("res://asset/art/scene/c01/s08_书店/夜晚版/fg_书店夜晚前景.png")
$"../ParallaxForeground/FGParallaxLayer/FGSprite2D".texture = preload(
"res://asset/art/scene/c01/s08_书店/夜晚版/fg_书店夜晚前景.png"
)
ladder.texture = preload("res://asset/art/scene/c01/s08_书店/夜晚版/e_梯子 夜晚.png")
@ -199,10 +205,9 @@ func _on_envelope_game_success() -> void:
func pay_off_wage() -> void:
SceneManager.enable_prop_item("prop_银元")
SceneManager.enable_important_item("prop_银元")
SceneManager.get_inspector().quit_and_hidden.connect(
_on_quit_inspect_coin, CONNECT_ONE_SHOT
)
SceneManager.get_inspector().quit_and_hidden.connect(_on_quit_inspect_coin, CONNECT_ONE_SHOT)
_check_portal()
func _on_quit_inspect_coin() -> void:
SceneManager.pop_os_with_str("c01_s08_获得袁大头后")

View File

@ -27,25 +27,26 @@ func _ready() -> void:
func _on_ground_ready() -> void:
var camera = SceneManager.get_camera_marker()
camera.limit_top = -1000
camera.limit_bottom = 158
await Util.wait(1.0)
# 相机抖动
camera.shake_camera()
# 不显示玩家,锁定玩家移动
SceneManager.freeze_player(0)
main_character = $"../DeployLayer/车夫与吕萍"
footstep_sfx = $"黄包车Sfx"
chapter_sfx = $chapter_sfx
SceneManager.get_camera_marker().limit_bottom = 158
SceneManager.get_camera_marker().limit_top = -158
SceneManager.set_camera_boundary(Rect2(0, -1000, 11360, 1158))
await get_tree().create_timer(1.0).timeout
# 相机抖动
SceneManager.get_camera_marker().shake_camera(8.0, 2.0)
# 注意第一段 dialog 在鬼差探头阶段播放
play("intro")
# ## test 测试最后运镜 ##
# main_character.global_position.x = target_x
# await get_tree().create_timer(1.0).timeout
# _on_finished()
func dialog1() -> void:
DialogueManager.dialogue_ended.connect(_start_running, CONNECT_ONE_SHOT)
# 注意第一段 dialog 在鬼差探头阶段播放
@ -102,6 +103,7 @@ func dialog3() -> void:
dialogue_c01, "c01_s11_车夫对话3", [GlobalConfig.DIALOG_IGNORE_INPUT]
)
func dialog4() -> void:
DialogueManager.show_dialogue_balloon(
dialogue_c01, "c01_s11_车夫对话4", [GlobalConfig.DIALOG_IGNORE_INPUT]
@ -126,17 +128,19 @@ func _on_finished():
tween.tween_property(marker, "global_position:y", target_y, building_duration)
tween.tween_callback(_show_chapter)
func final_dialog():
await get_tree().create_timer(2.0).timeout
await Util.wait(2.0)
DialogueManager.show_dialogue_balloon(
dialogue_c01, "c01_s11_车夫对话5", [GlobalConfig.DIALOG_IGNORE_INPUT]
)
# 展示章节转场
func _show_chapter():
chapter_sfx.play()
SceneManager.pop_center_texture(chapter_title)
get_tree().create_timer(3.5).timeout.connect(_transition)
Util.timer(3.5, _transition)
func _transition():

View File

@ -547,7 +547,7 @@ character = "小小蝶"
[node name="CameraFocusMarker" parent="Ground" index="6" node_paths=PackedStringArray("focusing_node")]
focusing_node = NodePath("../DeployLayer/车夫与吕萍")
force_offset = Vector2(50, -48)
force_offset = Vector2(30, -30)
[node name="点缀 黄包车车夫" type="Sprite2D" parent="Ground/ParallaxForeground/BGParallaxLayer" index="0"]
position = Vector2(687, 77)

View File

@ -56,7 +56,7 @@ func intro() -> void:
begger.play("杂戏团夜晚_小孩举碗呼吸")
# SceneManager.freeze_player(0)
# SceneManager.focus_node(begger, 5.)
get_tree().create_timer(1.).timeout.connect(_intro_dialog)
Util.timer(1., _intro_dialog)
func _intro_dialog() -> void:
@ -70,7 +70,7 @@ func _intro_dialog() -> void:
func ambush_begger_interacted():
begger.play("杂戏团夜晚_小孩放下碗")
SceneManager.lock_player(0)
await get_tree().create_timer(1.).timeout
await Util.wait(1)
DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s12_小孩放下碗")
await DialogueManager.dialogue_ended
SceneManager.unlock_player()
@ -83,7 +83,7 @@ func _on_interactable_bowl_interacted() -> void:
DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s12_鬼差来之前对话")
await DialogueManager.dialogue_ended
_ghost_move()
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
# 害怕过渡 -> 害怕
begger.play("举碗小孩害怕过渡")
var kids = $"../DeployLayer/其余小孩"
@ -91,7 +91,7 @@ func _on_interactable_bowl_interacted() -> void:
var kid = $"../DeployLayer/举腿小孩"
kid.play("夜晚举腿小孩杂耍到害怕过渡")
$"Sfx鬼差出场".play()
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s12_鬼差来了")
var tween = create_tween()
var camera = SceneManager.get_camera_marker()
@ -143,14 +143,12 @@ func player_been_catched(transition := true) -> void:
SceneManager.pop_debug_dialog_info("美术", "玩家被鬼差抓住")
# 等待动画播放完成
# TODO 添加转场前效果?
await get_tree().create_timer(1.5).timeout
var ground_loader = SceneManager.get_ground_loader()
await Util.wait(1.5)
# 传送到起点,继续跑
ground_loader.toggle_mask(true, Color.BLACK, 0.5)
await get_tree().create_timer(0.5).timeout
await SceneManager.toggle_ground_mask(true).finished
# 恢复花朵
flower.reset_all_blooming()
ground_loader.toggle_mask(false, Color.BLACK, 1.0)
await SceneManager.toggle_ground_mask(false).finished
# reset player and ghost position
ghost.global_position.x = ghost_start_x + 100.0
SceneManager.get_player().global_position.x = player_start_x

View File

@ -14,7 +14,7 @@ func _ready() -> void:
return
# if not data["received_letter"]:
# SceneManager.freeze_player(1.5)
# get_tree().create_timer(1.5).timeout.connect(_give_letter)
# Util.timer(1.5, _give_letter)
# elif GlobalConfig.DEBUG:
# print("_give_letter 已发放")

View File

@ -86,7 +86,7 @@ func _on_ground_ready() -> void:
if EventManager.get_stage("c02_madman_interacted") == 1:
SceneManager.lock_player(0)
# 等待转场
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
small_shoe.enabled = true
var y2 = small_shoe.position.y
var y1 = y2 - 150
@ -175,16 +175,16 @@ func eavesdrop() -> void:
# 12 c00_吕萍_蹲下 13 c00_吕萍_蹲下呼吸 14 c00_吕萍_起立
SceneManager.lock_player(0, 12)
# play("eavesdrop")
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
SceneManager.player_action(13)
# 对话
DialogueManager.show_dialogue_balloon(dialogue_c02, "c02_04_李氏癞子")
await DialogueManager.dialogue_ended
eavesdrop_window.visible = false
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
SceneManager.player_action(14, true)
ArchiveManager.set_global_entry(&"c02_eavesdrop_finished", true)
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
SceneManager.unlock_player()

View File

@ -27,5 +27,5 @@ func _on_closeup_tin_coin_exited(arg = null):
if arg == true:
ArchiveManager.set_global_entry(&"c02_tin_coin_taken", true)
SceneManager.enable_prop_item_silently("prop_锡箔元宝")
await get_tree().create_timer(0.5).timeout
await Util.wait(0.5)
SceneManager.enable_prop_item("prop_锡箔元宝")

View File

@ -44,7 +44,7 @@ func _on_ground_ready() -> void:
mice_hole.enabled = false
gas_light_upon_hole.lighted.connect(func():
mice_hole.enabled = true
get_tree().create_timer(1.5).timeout.connect(
Util.timer(1.5,
SceneManager.pop_os_with_str.bind("c02_二楼血脚印")
)
)

View File

@ -43,12 +43,4 @@ func take_off_flyer(immediatelly = false):
flyer.visible = false
var hand = $"../DeployLayer/小手讨东西"
hand.enabled = true
# if hand.icount == 0:
# await get_tree().create_timer(1.0).timeout
# hand.icount += 1
# SceneManager.lock_player()
# await hand.do_first_interact(immediatelly)
# SceneManager.unlock_player()

View File

@ -98,7 +98,7 @@ func eavesdrop_start() -> void:
var node = $"../DeployLayer/瞎子小蝉对话"
node.visible = true
# 偷听动作
await get_tree().create_timer(2.0).timeout
await Util.wait(2.0)
# 动作后先出声音
DialogueManager.show_dialogue_balloon(dialogue_c02, "c02_05_瞎子与小蝉")
var tween = create_tween()

View File

@ -36,7 +36,7 @@ func _on_ground_ready() -> void:
var half_length = rope_length / 2.0
rope_range.x = rope.global_position.x - half_length
rope_range.y = rope.global_position.x + half_length
await get_tree().create_timer(0.1).timeout
await Util.wait(0.1)
var player = %MainPlayer
# 玩家位置更新时,更新麻绳隆起
player.position_updated.connect(_on_player_position_updated)

View File

@ -24,12 +24,12 @@ func knock_light_door():
for _i in range(100):
AudioManager.play_sfx(knock_stream)
SceneManager.lock_player(0, 6)
await get_tree().create_timer(0.5).timeout
await Util.wait(0.5)
# 被抓时打断
if not player.visible or player.hide_sprite:
_outtro_show()
break
await get_tree().create_timer(0.5).timeout
await Util.wait(0.5)
# 被抓时打断
if not player.visible or player.hide_sprite:
_outtro_show()
@ -42,10 +42,10 @@ func _outtro_show():
# 禁止猪头怪动画播放结束后重开游戏
$"../DeployLayer/追猫猪头怪_传送".allow_restart_game = false
$"Sfx结尾演出".play()
await get_tree().create_timer(8.0).timeout
await Util.wait(8.0)
$"../盒子猫CanvasLayer".show_footprint()
$"Sfx脚印".play()
# 盒子猫逃脱成功/被抓走,游戏结束,解锁瞎子卧室门
await get_tree().create_timer(10.0).timeout
await Util.wait(10.0)
ArchiveManager.set_global_entry(&"c02_the_blind_room_unlocked", true)
SceneManager.get_ground_loader().transition_to_scene("c02_s08", "1")

View File

@ -22,7 +22,7 @@ func play_bgm():
func pre_finished():
# 在 animation player 结束前 1 秒调用
CanvasUtil.shake_layer(self, 2.0, 4.0, 15.0)
Util.shake_layer(self, 2.0, 4.0, 15.0)
# debug 模式允许跳过
if GlobalConfig.DEBUG:
DialogueManager.show_dialogue_balloon(dialogue_c01, "c01_s11_车夫对话1")

View File

@ -71,8 +71,8 @@ func _on_interacted() -> void:
$SfxOpen.play()
var inspect_texture = preload("res://asset/art/scene/c01/s07_书店外/纸片_正面.png")
var res = preload("res://asset/dialogue/inspect_content.dialogue")
var lines = await DialogueUtil.get_lines(res, "c01_飘落的寻人启事")
var note = DialogueUtil.concact_content_from_lines(lines)
var lines = await Util.get_lines(res, "c01_飘落的寻人启事")
var note = Util.concact_content_from_lines(lines)
SceneManager.get_inspector().pop_standard_inspection(inspect_texture, null, note, false)

View File

@ -135,14 +135,14 @@ func _gaslight_interacted():
else:
# 高位 5 号
SceneManager.freeze_player(2.0, 5)
await get_tree().create_timer(0.8).timeout
await Util.wait(0.8)
sfx_success.play()
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
_switch_gaslight(true, true)
lighted.emit()
if not ArchiveManager.get_global_value(&"c02_gaslight_first_lighted"):
ArchiveManager.set_global_entry(&"c02_gaslight_first_lighted", true)
var scene = ArchiveManager.archive.current_scene
if scene == "c02_s02" or scene == "c02_s03":
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
SceneManager.pop_os_with_str("c02_使用煤油灯")

View File

@ -52,6 +52,7 @@ texture = SubResource("ImageTexture_cffxi")
stretch_mode = 5
[node name="Sfx撕海报" type="AudioStreamPlayer" parent="."]
process_mode = 1
stream = ExtResource("5_xjg44")
bus = &"game_sfx"
script = ExtResource("6_cffxi")

View File

@ -66,7 +66,7 @@ var icount: int:
# 9 小手交互,吓摔倒
SceneManager.lock_player(0, 9, true)
SceneManager.pop_os_with_str("c02_小手出现摔倒")
get_tree().create_timer(1.5).timeout.connect($"Sfx摔倒".play)
Util.timer(1.5, $"Sfx摔倒".play)
await SceneManager.get_player().animation_finished
SceneManager.unlock_player()
elif stage == 1:
@ -194,7 +194,7 @@ func _on_interacted() -> void:
SceneManager.pop_debug_dialog_info(
"美术", "叠成纸杯: 小手_锡箔_hide + 小手_纸杯_show + 小手_纸杯_idle"
)
await get_tree().create_timer(2).timeout
await Util.wait(2.0)
# 稍等片刻再给小鞋子
SceneManager.enable_prop_item("prop_小鞋子1")
await SceneManager.get_inspector().quit_and_hidden
@ -210,7 +210,7 @@ func _on_interacted() -> void:
# 先设置允许 pickable然后再设置可见性等待动画播完再显示
tin_coin_drop = true
coin.visible = false
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
coin.visible = true
if not ArchiveManager.get_global_value(&"c02_little_hand_dropped_tin"):
SceneManager.pop_os_with_str("c02_小手要东西")
@ -224,7 +224,7 @@ func _on_interacted() -> void:
SceneManager.disable_prop_item("prop_老虎钳")
animated_sprite.play("小手_老虎钳_hide")
SceneManager.pop_debug_dialog_info("美术", "小手_老虎钳_hide + 小手_弹珠_show + 小手_弹珠_idle")
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
holding_prop = "prop_弹珠"
EventManager.set_stage_if_greater("handnote_stage", 4)
elif id == 0 and key == "prop_弹珠" and not pliers_to_ball:
@ -233,7 +233,7 @@ func _on_interacted() -> void:
SceneManager.disable_prop_item("prop_弹珠")
animated_sprite.play("小手_弹珠_hide")
SceneManager.pop_debug_dialog_info("美术", "小手_弹珠_hide + 小手_老虎钳_show + 小手_老虎钳_idle")
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
holding_prop = "prop_老虎钳"
if interacted_success:

View File

@ -59,7 +59,7 @@ func _on_button_pressed() -> void:
button.disabled = true
# 肉落下的声音,然后自动退出界面
SceneManager.pop_debug_dialog_info("音效", "肉落下的声音(敲门游戏成功)")
await get_tree().create_timer(2.0).timeout
await Util.wait(2)
exit.emit(true)
else:
SceneManager.pop_debug_dialog_info("音效", "敲门:" + str(current_id))

View File

@ -25,7 +25,7 @@ func _ready() -> void:
tween.tween_property(child_sprite, "modulate:a", 0.0, 0.5)
else:
child_sprite.modulate.a = 0
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
_try_ripple()
func _try_ripple():

View File

@ -28,7 +28,7 @@ func _on_area_entered(_body):
if not _body is MainPlayer:
return
label.visible = true
await get_tree().create_timer(randf()).timeout
await Util.wait(randf())
var tween = create_tween()
tween.tween_property(label, "position:y", 3.0, 0.6).set_trans(Tween.TRANS_BOUNCE)
tween.tween_property(label, "position:y", 0.0, 0.6).set_trans(Tween.TRANS_BOUNCE)

View File

@ -72,7 +72,7 @@ func start():
#### part3: 小蝉捂眼,瞎子、胖子声音
# 小蝉身体回正、捂眼
DialogueManager.show_dialogue_balloon(dialogue_c02, "c02_s11_谢幕交谈2")
get_tree().create_timer(5.0).timeout.connect(_hand_cover_eyes)
Util.timer(5.0, _hand_cover_eyes)
await DialogueManager.dialogue_ended
SceneManager.pop_debug_dialog_info("音效", "小蝉笑声/耳鸣")
await create_tween().tween_interval(3.0).finished
@ -100,9 +100,9 @@ func _on_got_dialogue(line: DialogueLine):
if speak:
if first_speak:
first_speak = false
await get_tree().create_timer(2.0).timeout
await Util.wait(2)
else:
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
toggle_speak(speak)

View File

@ -92,7 +92,7 @@ func _on_ground_ready():
# 禁用传送过来的 portal 3s
_disable_portal_a_few_seconds(portal_node)
# 3 秒后传送
get_tree().create_timer(1.0).timeout.connect(_do_transfer)
Util.timer(1.0, _do_transfer)
if GlobalConfig.DEBUG:
print("猪头怪 Transfer to: " + portal)
else:
@ -108,7 +108,7 @@ func _on_footstep_timer_timeout() -> void:
footstep_count += 1
if footstep_count % 2 == 0:
# shake camera
camera.shake_camera(4.0, 5.0)
camera.shake_camera()
if abs(signed_x_diff) > 250:
if footstep_count % 3 == 0:
sfx_footstep.play()
@ -177,7 +177,7 @@ func do_catch(front: bool):
# 其次 catch_nearby 播放 猪头怪抓盒子猫后段
sprite2d.play("猪头怪抓盒子猫后段")
# 被抓音效
get_tree().create_timer(0.5).timeout.connect($"Sfx猫被抓".play)
Util.timer(0.5, $"Sfx猫被抓".play)
# 呼吸声渐隐
$"Sfx喘气".easing_kill()
SceneManager.pop_debug_dialog_info("音效", "抓取过程。 从捡小猫开始=" + str(front))

View File

@ -249,7 +249,7 @@ func _success() -> void:
func _on_target_book_pressed():
$"Shelf/生死簿".play("书架扶正")
await get_tree().create_timer(1.5).timeout
await Util.wait(1.5)
$"生死簿".show()
# 完成后释放信号
$"生死簿".finished.connect(_on_success_finished)

View File

@ -119,7 +119,7 @@ func _on_drawer_picked(_node):
box_animation.play()
drawer_btn.visible = false
await box_animation.animation_finished
await get_tree().create_timer(1.0).timeout
await Util.wait(1)
box_animation.visible = false
box_broken.visible = false
box_drawer.visible = true
@ -200,7 +200,7 @@ func _on_wheel_rotated(radiant: float) -> void:
sfx_music.play()
else:
sfx_music.stream_paused = false
get_tree().create_timer(playing_step_sec).timeout.connect(_on_playing_step_finished)
Util.timer(playing_step_sec, _on_playing_step_finished)
func _on_playing_step_finished():
@ -211,7 +211,7 @@ func _on_playing_step_finished():
return
if continue_playing:
continue_playing = false
get_tree().create_timer(playing_step_sec).timeout.connect(_on_playing_step_finished)
Util.timer(playing_step_sec, _on_playing_step_finished)
else:
playing = false
sfx_music.stream_paused = true
@ -226,7 +226,7 @@ func _checkout_auto_play():
# var audio_len = audio_auto.get_length()
# 音效 9.5 秒时关闭盒子
var audio_len = 9.5
get_tree().create_timer(audio_len).timeout.connect(_on_auto_play_finished)
Util.timer(audio_len, _on_auto_play_finished)
func _on_auto_play_finished():

View File

@ -66,7 +66,7 @@ func _ready() -> void:
# # test close shaking effect
# ArchiveManager.archive.bayinhe_mode = "playing"
# _chechout_mode(true)
# get_tree().create_timer(1.0).timeout.connect(_on_auto_play_finished)
# Util.timer(1.0, _on_auto_play_finished)
func _on_visibility_changed() -> void:
@ -224,7 +224,7 @@ func _on_wheel_rotated(radiant: float) -> void:
audio_player.play()
else:
audio_player.stream_paused = false
get_tree().create_timer(playing_step_sec).timeout.connect(_on_playing_step_finished)
Util.timer(playing_step_sec, _on_playing_step_finished)
func _on_playing_step_finished():
@ -235,7 +235,7 @@ func _on_playing_step_finished():
return
if continue_playing:
continue_playing = false
get_tree().create_timer(playing_step_sec).timeout.connect(_on_playing_step_finished)
Util.timer(playing_step_sec, _on_playing_step_finished)
else:
playing = false
audio_player.stream_paused = true
@ -250,7 +250,7 @@ func _checkout_auto_play():
# var audio_len = audio_auto.get_length()
# 音效 9.5 秒时关闭盒子
var audio_len = 9.5
get_tree().create_timer(audio_len).timeout.connect(_on_auto_play_finished)
Util.timer(audio_len, _on_auto_play_finished)
func _on_auto_play_finished():

View File

@ -245,13 +245,13 @@ func game_win() -> void:
# 0:默认 1:寻找弹珠(老虎钳可以换弹珠) 2:给出弹珠 3:游戏结束
ArchiveManager.set_global_entry(&"c02_ball_game_stage", 3)
%"Sfx结尾小孩跑".play()
CanvasUtil.shake_layer(self, 2.0)
Util.shake_layer(self, 2.0)
# 弹珠雨
$BallsFalling.emitting = true
pivot.visible = false
DialogueManager.show_dialogue_balloon_scene(self, dialogue_c02, "c02_弹珠游戏4")
await ball_dialogue_ended
await get_tree().create_timer(3.0).timeout
await Util.wait(3.0)
exit.emit(true)
@ -303,15 +303,4 @@ func apply_dialogue_line() -> void:
tween.tween_property(label, "modulate:a", 1.0, 0.5)
tween.tween_interval(2.0)
tween.tween_property(label, "modulate:a", 0.0, 0.5)
# # 因为版权问题,有些 mp3 文件打不开,所以使用 ogg 格式
# var audio_path = "res://asset/audio/peiyin/ogg/%s.ogg" % translation_key
# if FileAccess.file_exists(audio_path):
# var stream = load(audio_path)
# audio_player.stream = stream
# audio_player.play()
# await audio_player.finished
# await get_tree().create_timer(1.0).timeout
# else:
# push_warning("No audio file found for " + translation_key)
# await get_tree().create_timer(3.0).timeout
ball_dialogue_ended.emit()

View File

@ -32,7 +32,7 @@ func _ready() -> void:
func _enter_tree() -> void:
SceneManager.pop_center_notification(tr("input_拼凑信件"))
await get_tree().create_timer(4.0).timeout
await Util.wait(4.0)
SceneManager.pop_center_notification(tr("ui_press_q_to_exit"))
func _setup() -> void:
@ -144,7 +144,7 @@ func _post_success():
SceneManager.enable_important_item("prop_院长的信")
whole.visible = false
# 1s 后退出
await get_tree().create_timer(1.0).timeout
await Util.wait(1.0)
if get_parent() != null:
success.emit()
get_parent().remove_child(self)

View File

@ -76,9 +76,9 @@ func _ready():
# navigation
return_btn.pressed.connect(_on_return_btn_pressed)
return_btn.grab_focus()
SceneManager.toggle_pause_counter(true)
# open
$"Sfx打开".play()
SceneManager.toggle_pause_counter(true)
func _on_master_bus_slider_value_changed(value: float) -> void:

View File

@ -11,7 +11,7 @@ func run_clip(card_mode: bool):
tween.tween_interval(5.0)
tween.tween_property($"", "modulate:a", 1.0, 5.0)
if not card_mode:
await get_tree().create_timer(3.0).timeout
await Util.wait(3.0)
# 增加配音
dialog_node = DialogueManager.show_dialogue_balloon(dialogue_res, "c02_s11_井边疯子对话")
dialog_node.process_mode = Node.PROCESS_MODE_ALWAYS

View File

@ -12,10 +12,10 @@ func _ready() -> void:
$SfxOpen.play()
layer = GlobalConfig.CANVAS_LAYER_UX_PANEL
SceneManager.toggle_pause_counter(true)
continue_btn.pressed.connect(quit)
continue_btn.pressed.connect(_continue)
note_btn.pressed.connect(SceneManager.show_note)
settings_btn.pressed.connect(SceneManager.show_settings)
main_menu_btn.pressed.connect(SceneManager.checkout_index_page)
main_menu_btn.pressed.connect(_index_menu)
quit_btn.pressed.connect(_quit_game)
func _quit_game() -> void:
@ -23,16 +23,22 @@ func _quit_game() -> void:
ArchiveManager.notification(NOTIFICATION_WM_CLOSE_REQUEST)
# 退出游戏过程隐藏界面
# 隐藏 panel
quit()
_continue()
func quit():
$SfxOpen.global_play()
func _continue():
$SfxClose.global_play()
SceneManager.toggle_pause_counter(false)
queue_free()
func _index_menu():
SceneManager.checkout_index_page()
# 允许 ground loader 运行 transition
_continue()
func _unhandled_input(event: InputEvent) -> void:
# panel 界面接受所有输入事件
get_viewport().set_input_as_handled()
if event.is_action_pressed("cancel") or event.is_action_pressed("escape"):
quit()
_continue()

View File

@ -99,7 +99,7 @@ theme_override_fonts/font = ExtResource("4_mns6r")
theme_override_font_sizes/font_size = 20
theme_override_styles/hover = SubResource("StyleBoxLine_v1oku")
theme_override_styles/pressed = SubResource("StyleBoxLine_a3uyd")
text = "ux_panel_笔记"
text = "ux_panel_线索笔记"
flat = true
[node name="MarginContainer3" type="MarginContainer" parent="Bag"]

View File

@ -81,10 +81,23 @@ func _on_tab_memory_pressed():
root.quit()
func _exit_tree() -> void:
sfx_close.global_play()
var play_close_on_exit_tree = true
# func _unhandled_input(event: InputEvent) -> void:
# if event.is_action_pressed("memory") and current_type != "回忆":
# get_viewport().set_input_as_handled()
# SceneManager.show_memory()
func _exit_tree() -> void:
if play_close_on_exit_tree:
sfx_close.global_play()
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("memory") and current_type != "回忆":
get_viewport().set_input_as_handled()
play_close_on_exit_tree = false
_on_tab_memory_pressed()
elif event.is_action_pressed("note") and current_type != "线索":
get_viewport().set_input_as_handled()
play_close_on_exit_tree = false
_on_tab_note_pressed()
elif event.is_action_pressed("bag") and current_type != "物品":
get_viewport().set_input_as_handled()
play_close_on_exit_tree = false
_on_tab_bag_pressed()

View File

@ -1,14 +0,0 @@
class_name CanvasUtil extends Object
static func shake_layer(layer: CanvasLayer, duration: float, delta := 2.0, fps := 20.0) -> void:
var tween = layer.create_tween()
# shake layer's offset
var origin_offset = layer.offset
var count = int(duration * fps)
var delta_t = 1.0 / fps
for i in range(count):
var offset = Vector2(randf_range(-delta, delta), randf_range(-delta, delta))
tween.tween_property(layer, "offset", origin_offset + offset, delta_t)
# tween back to origin
tween.tween_property(layer, "offset", origin_offset, delta_t)

View File

@ -1 +0,0 @@
uid://l4egv2jc0vjb

View File

@ -1,22 +0,0 @@
class_name DebugLabel extends Label
@export var enabled := false:
set(value):
enabled = value
if enabled:
_display()
else:
_hide()
func _ready() -> void:
if not GlobalConfig.DEBUG:
return
func _hide():
visible = false
func _display():
pass

View File

@ -1 +0,0 @@
uid://cgi8uhk7ii5o6

View File

@ -1,30 +0,0 @@
class_name DialogueUtil extends RefCounted
static func concact_content_from_lines(lines:=[]):
if lines.is_empty():
return ""
var content = ""
for i in range(len(lines) - 1):
var line = lines[i] as DialogueLine
content += TranslationServer.tr(line.translation_key) + "\n"
# last line without "\n"
content += TranslationServer.tr(lines[-1].translation_key)
return content
static func get_lines(res: DialogueResource, title: String) -> Array:
var lines = []
var current_line = await res.get_next_dialogue_line(title)
while current_line:
lines.append(current_line)
if current_line.next_id != "end":
current_line = await res.get_next_dialogue_line(current_line.next_id)
else:
break
return lines
# pop os lines
static func generate_lines(content: String) -> Array:
var text = "~ title\n" + content + "\n=> END"
var res = DialogueManager.create_resource_from_text(text)
return await get_lines(res, "title")

View File

@ -1 +0,0 @@
uid://ckwvshyrj4vns

58
util/util.gd Normal file
View File

@ -0,0 +1,58 @@
extends Node
#### Timer
func wait(duration: float) -> void:
await get_tree().create_timer(duration).timeout
func timer(duration: float, callable: Callable) -> SceneTreeTimer:
var t = get_tree().create_timer(duration, false)
t.timeout.connect(callable)
return t
#### Canvas
func shake_layer(layer: CanvasLayer, duration: float, delta := 2.0, fps := 20.0) -> void:
var tween = layer.create_tween()
# shake layer's offset
var origin_offset = layer.offset
var count = int(duration * fps)
var delta_t = 1.0 / fps
for i in range(count):
var offset = Vector2(randf_range(-delta, delta), randf_range(-delta, delta))
tween.tween_property(layer, "offset", origin_offset + offset, delta_t)
# tween back to origin
tween.tween_property(layer, "offset", origin_offset, delta_t)
###### Dialogue
func concact_content_from_lines(lines := []):
if lines.is_empty():
return ""
var content = ""
for i in range(len(lines) - 1):
var line = lines[i] as DialogueLine
content += TranslationServer.tr(line.translation_key) + "\n"
# last line without "\n"
content += TranslationServer.tr(lines[-1].translation_key)
return content
func get_lines(res: DialogueResource, title: String) -> Array:
var lines = []
var current_line = await res.get_next_dialogue_line(title)
while current_line:
lines.append(current_line)
if current_line.next_id != "end":
current_line = await res.get_next_dialogue_line(current_line.next_id)
else:
break
return lines
# pop os lines
func generate_lines(content: String) -> Array:
var text = "~ title\n" + content + "\n=> END"
var res = DialogueManager.create_resource_from_text(text)
return await get_lines(res, "title")

1
util/util.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://cqkbvu00n5ao8