From 85392b08ea5a3ec7d9bff192c9059f5fe742a58e Mon Sep 17 00:00:00 2001 From: cakipaul Date: Thu, 24 Jul 2025 15:52:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AB=E9=9F=B3=E7=9B=92=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=EF=BC=9Bdraggable=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- asset/dialogue/item_description.csv | 3 +- asset/dialogue/item_description.dialogue | 3 +- scene/little_game/general/draggable.gd | 149 ++++++++++++++--------- scene/little_game/八音盒/八音盒.gd | 1 + 4 files changed, 97 insertions(+), 59 deletions(-) diff --git a/asset/dialogue/item_description.csv b/asset/dialogue/item_description.csv index 2901387a..5aeb154a 100644 --- a/asset/dialogue/item_description.csv +++ b/asset/dialogue/item_description.csv @@ -36,7 +36,8 @@ ui_center_notify_use_prop,按 E 使用道具,,,,,Press E to use item ui_center_notify_check_note,按 N 查看线索笔记,,,,,Press N to check clue notes ui_center_notify_check_bag,按 B 查看重要物品,,,,,Press B to check important items ui_center_notify_right_click_prop,右键点击可检阅道具,,,,,Right-click to examine item -ui_left_mouse_shave,按住鼠标拖拽可刮开海报,,,,,Hold and drag mouse to scrape off poster +ui_center_notify_drag_to_rotate,按住拖拽可旋转把手,,,,,Hold and drag to rotate the handle +ui_left_mouse_shave,按住拖拽可刮开海报,,,,,Hold and drag to scrape off poster ui_switch_prop,点击图标可切换道具(或按 Z/C),,,,,Click icon to switch items (or press Z/C) ui_press_shift,按住 Shift 奔跑,,,,,Hold Shift to run ui_boxcat_press_s,按住 S 躲藏,,,,,Hold S to hide diff --git a/asset/dialogue/item_description.dialogue b/asset/dialogue/item_description.dialogue index ddfa4fb6..d689d35f 100644 --- a/asset/dialogue/item_description.dialogue +++ b/asset/dialogue/item_description.dialogue @@ -46,7 +46,8 @@ 按 N 查看线索笔记 [ID:ui_center_notify_check_note] 按 B 查看重要物品 [ID:ui_center_notify_check_bag] 右键点击可检阅道具 [ID:ui_center_notify_right_click_prop] -按住鼠标拖拽可刮开海报 [ID:ui_left_mouse_shave] +按住拖拽可旋转把手 [ID:ui_center_notify_drag_to_rotate] +按住拖拽可刮开海报 [ID:ui_left_mouse_shave] 点击图标可切换道具(或按 Z/C) [ID:ui_switch_prop] 按住 Shift 奔跑 [ID:ui_press_shift] 按住 S 躲藏 [ID:ui_boxcat_press_s] diff --git a/scene/little_game/general/draggable.gd b/scene/little_game/general/draggable.gd index 51371cbf..49679e68 100644 --- a/scene/little_game/general/draggable.gd +++ b/scene/little_game/general/draggable.gd @@ -33,7 +33,27 @@ signal dropped(node: Draggable2D) sprite.texture = texture @export var limit_rect := Rect2(Vector2.ZERO, Vector2(564, 316)) -@onready var sprite = $Sprite2D as Sprite2D +@onready var sprite: Sprite2D = $Sprite2D + +# Whether the item is currently being held by the player +var holding := false: + set(val): + if holding != val: + holding = val +var touching := false + +# 静态变量优化:使用对象引用而非字符串 +static var current_focusing_node: Draggable2D = null +static var pending_enter_callables: Array[Callable] = [] + +# 缓存常量 +const OUTLINE_THICKNESS := 1.0 +const OUTLINE_TWEEN_DURATION := 0.2 +const BUTTON_ALPHA_DURATION := 0.15 + +# 缓存变量 +var _outline_tween: Tween +var _button_tween: Tween func _ready() -> void: @@ -42,25 +62,18 @@ func _ready() -> void: if Engine.is_editor_hint(): return # 初始化隐藏白边 - sprite.material.set("shader_parameter/thickness", 0.0) - mouse_entered.connect(_on_mouse_entered) - mouse_exited.connect(_on_mouse_exited) - - -# Whether the item is currently being held by the player -var holding = false -var touching = false - -static var current_focusing_item = "": - set(val): - current_focusing_item = val - if GlobalConfig.DEBUG: - print("current_focusing_item=", current_focusing_item) -static var pending_enter_callables := [] as Array[Callable] + sprite.material.set_shader_parameter("thickness", 0.0) + + # 安全检查 + if has_signal("mouse_entered"): + mouse_entered.connect(_on_mouse_entered) + mouse_exited.connect(_on_mouse_exited) + else: + printerr("Draggable2D: mouse_entered or mouse_exited signal not found.") func is_focused() -> bool: - return current_focusing_item == item_name + return current_focusing_node == self func _on_mouse_entered() -> bool: @@ -69,12 +82,14 @@ func _on_mouse_entered() -> bool: return false if holding or is_focused(): return true - # 尝试获得 current_focusing_item - if current_focusing_item != "": + + # 尝试获得 current_focusing_node + if current_focusing_node: if not pending_enter_callables.has(_on_mouse_entered): pending_enter_callables.append(_on_mouse_entered) return false - current_focusing_item = item_name + + current_focusing_node = self _toggle_outline(true) return true @@ -82,53 +97,51 @@ func _on_mouse_entered() -> bool: func _on_mouse_exited() -> void: touching = false pending_enter_callables.erase(_on_mouse_entered) - # frezzing 不影响 mouse exited - if is_focused(): - current_focusing_item = "" - for c in pending_enter_callables: + # freezing 不影响 mouse exited + if is_focused() and not holding: + current_focusing_node = null + while pending_enter_callables.size() > 0: + var c = pending_enter_callables.pop_front() if c.call(): break _toggle_outline(false) -func _notification(what: int) -> void: - if what == NOTIFICATION_PREDELETE: - if holding: - _drop() - elif touching: - _on_mouse_exited() - func _input(event: InputEvent) -> void: if freezing or Engine.is_editor_hint() or not is_visible_in_tree(): return - if event is InputEventMouseButton: - if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: - # Trigger the pick action - if touching and not holding: - _try_pick() - elif holding: - _drop() + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed: + if is_focused() and not holding: + get_viewport().set_input_as_handled() + _try_pick.call_deferred() + elif holding: + get_viewport().set_input_as_handled() + _drop() elif holding and event is InputEventMouseMotion: _update_pos_to_mouse() -func _update_pos_to_mouse(): +func _update_pos_to_mouse() -> void: # update global position - global_position = get_global_mouse_position() - global_position.x = clamp(global_position.x, limit_rect.position.x, limit_rect.end.x) - global_position.y = clamp(global_position.y, limit_rect.position.y, limit_rect.end.y) + var target_pos = get_global_mouse_position() + global_position.x = clamp(target_pos.x, limit_rect.position.x, limit_rect.end.x) + global_position.y = clamp(target_pos.y, limit_rect.position.y, limit_rect.end.y) func _try_pick() -> void: if act_as_button: # 作为按钮,发送 picked 信号 picked.emit(self) - var tween = create_tween() - tween.tween_property(sprite.material, "shader_parameter/alpha_ratio", 1.0, 0.15) + if _button_tween and _button_tween.is_running(): + _button_tween.kill() + _button_tween = create_tween() + _button_tween.tween_property(sprite.material, "shader_parameter/alpha_ratio", 1.0, BUTTON_ALPHA_DURATION) return - if current_focusing_item != item_name: + + if not is_focused(): return + # reset rotation rotation = 0 _toggle_outline(false) @@ -139,25 +152,47 @@ func _try_pick() -> void: func _drop() -> void: + if touching: + _toggle_outline(true) if holding: holding = false + if not touching: + current_focusing_node = null + # 清空数组前创建副本,避免在迭代时修改数组 + var callables_copy = pending_enter_callables.duplicate() + for c in callables_copy: + if c.call(): + break # z_index -= 1 dropped.emit(self) - if is_focused(): - _toggle_outline(true) - else: - # not touching but dropped, remove current_focusing_item if any - current_focusing_item = "" -func _toggle_outline(display = true): - var tween = create_tween() - if display: - tween.tween_property(sprite.material, "shader_parameter/thickness", 1.0, 0.2) - else: - tween.tween_property(sprite.material, "shader_parameter/thickness", 0.0, 0.2) +func _toggle_outline(display: bool) -> void: + # 避免重复创建 tween + if _outline_tween and _outline_tween.is_running(): + _outline_tween.kill() + + _outline_tween = create_tween() + var target_thickness := OUTLINE_THICKNESS if display else 0.0 + _outline_tween.tween_property( + sprite.material, "shader_parameter/thickness", target_thickness, OUTLINE_TWEEN_DURATION + ) func _exit_tree() -> void: if touching: - _on_mouse_exited() \ No newline at end of file + _on_mouse_exited() + # 清理静态引用,避免内存泄漏 + if current_focusing_node == self: + current_focusing_node = null + + +func force_hold() -> void: + if holding: + return + if not is_focused() and current_focusing_node: + current_focusing_node._drop() + _toggle_outline(false) + current_focusing_node = self + holding = true + picked.emit(self) \ No newline at end of file diff --git a/scene/little_game/八音盒/八音盒.gd b/scene/little_game/八音盒/八音盒.gd index 20e36d76..d70c0d0d 100644 --- a/scene/little_game/八音盒/八音盒.gd +++ b/scene/little_game/八音盒/八音盒.gd @@ -291,6 +291,7 @@ func _chechout_stage(s: int, play_sfx := true) -> void: if play_sfx: sfx_open_lid.play() 3: + SceneManager.pop_center_notification("ui_center_notify_drag_to_rotate") box_opened.visible = true d4.visible = true if pic: