diff --git a/asset/dialogue/item_description.csv b/asset/dialogue/item_description.csv index 4dfbacb9..e55bd116 100644 --- a/asset/dialogue/item_description.csv +++ b/asset/dialogue/item_description.csv @@ -39,7 +39,8 @@ ui_center_notify_lack_of_prop,缺少道具,,,,, 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 16e1ab7a..4b26f264 100644 --- a/asset/dialogue/item_description.dialogue +++ b/asset/dialogue/item_description.dialogue @@ -48,7 +48,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 796dd616..9178338d 100644 --- a/scene/little_game/general/draggable.gd +++ b/scene/little_game/general/draggable.gd @@ -11,6 +11,7 @@ signal dropped(node: Draggable2D) @export var act_as_button := false # z +1 when picked (-1 when dropped) # @export var z_up_on_picked := true +@export var item_name = "" @export var sprite_offset := Vector2(0, 0): set(val): sprite_offset = val @@ -32,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: @@ -41,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("Draggable2D 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 == name + return current_focusing_node == self func _on_mouse_entered() -> bool: @@ -68,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 = name + + current_focusing_node = self _toggle_outline(true) return true @@ -81,55 +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: - get_viewport().set_input_as_handled() - _try_pick() - elif holding: - get_viewport().set_input_as_handled() - _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 != name: + + if not is_focused(): return + # reset rotation rotation = 0 _toggle_outline(false) @@ -140,25 +152,48 @@ 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 88852be3..07b0d826 100644 --- a/scene/little_game/八音盒/八音盒.gd +++ b/scene/little_game/八音盒/八音盒.gd @@ -290,6 +290,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: