@tool extends Marker2D class_name SignSnapper signal arrived @export var enabled := true # 仅使用 x 坐标;否则也用 y 进行 walk to @export var use_x_only := true # 允许小幅调动,0-3px 被忽视,3-5px 被视作 5px @export var auto_adjust := true # x 左右有效范围 @export_range(0.0, 20.0, 0.1) var radius := 0.0 # 让玩家走到边缘,特别适用于 npc 对话等情景 @export var walk_to_edge := false @export var action_on_arrived := 3 @export_tool_button("debug 检查玩家触发位置") var debug_check_player_pos = _debug_check_player_pos var detacted_sign: Sign # arrived 延时: delay_arrived-arrived static var delay_arrived := 0.0 static var delay_after_action := 0.0 # 玩家在和 sign 交互时,如果读到节点中有 SignSnapper,则移动到该位置(x 对齐) func _ready(): if not get_parent(): return for c in get_parent().get_children(): if c is Sign: detacted_sign = c break if not detacted_sign and Engine.is_editor_hint(): printerr("SignSnapper must have a Sign sibling node!") if not Engine.is_editor_hint(): detacted_sign.interacted.connect(_on_interacted) func _on_interacted(): if not detacted_sign or not enabled: return var player = SceneManager.get_player() var target_pos = global_position if use_x_only: target_pos.y = player.global_position.y var player_x = player.global_position.x if (radius > 0 and absf(target_pos.x - player_x) > radius) or walk_to_edge: # clamp in range if target_pos.x > player_x: target_pos.x -= radius else: target_pos.x += radius var diff = absf(player_x - target_pos.x) if auto_adjust: # 允许小幅调动,0-3px 被忽视,3-5px 被视作 5px if diff < 3: target_pos.x = player_x diff = 0 elif diff < 5: target_pos.x = player_x + (5 if target_pos.x > player_x else -5) diff = 5 var tween if diff == 0: tween = create_tween() else: tween = player.walk_to(target_pos) if delay_arrived > 0: tween.tween_interval(delay_arrived) if action_on_arrived != 3: tween.tween_callback(SceneManager.player_action.bind(action_on_arrived)) if delay_after_action > 0: tween.tween_interval(delay_after_action) tween.tween_callback(arrived.emit) func _debug_check_player_pos(): if not Engine.is_editor_hint(): return var grounds = get_tree().get_nodes_in_group("ground") if not grounds: printerr("no ground") return var ground = grounds[0] if use_x_only: ground.replace_player_to_portal = true var player = ground.player if radius > 0: var player_x = player.global_position.x if absf(player_x - global_position.x) > radius: # clamp in range if player_x < global_position.x: player.global_position.x = global_position.x - radius else: player.global_position.x = global_position.x + radius else: player.global_position.x = global_position.x if not use_x_only: player.global_position.y = global_position.y