class_name Hook extends Node2D ## ================ ## Export Field ## ================ @export var min_length := 140 @export var max_length := 200 @export var streching_speed:float = 1400 ## 线段 @onready var line_2d: Line2D = %Line2D ## 检测碰撞的区域 @onready var area_2d: Area2D = %Area2D const GRAPABLE_GROUP = &"GRAPABLE" const ANCHOR_NODE = preload("uid://dfm5wy1rmci68") signal stretching_finished(reach_limit:bool, anchor_node: Node2D) ## ================ ## Private Field ## ================ var _binded_hook_comp var _is_stretching: bool = false var _stretching_dir: Vector2 = Vector2.ZERO var _cached_cancel: bool = false var _anchor: Node2D func _ready() -> void: area_2d.position = Vector2.ZERO area_2d.area_entered.connect(grap_detected) area_2d.body_entered.connect(grap_detected) ##初始化 func init(hook_comp:SpawnHookComponet,reset_to_target: bool): _binded_hook_comp = hook_comp if reset_to_target: var p : Vector2= hook_comp.owner.global_position self.global_position = p ##开始stretching func start_stretching(direction: Vector2) -> void: _is_stretching = true _cached_cancel = false _stretching_dir = direction ##结束stretching func end_stretching(force_end: bool = false) -> bool: ##如果还没有达到最短的stretching length,则继续 if not force_end and _is_stretching: var d = self.global_position.distance_to(area_2d.global_position) if d < min_length: _cached_cancel = true return false _is_stretching = false _stretching_dir = Vector2.ZERO return true ##是否正在stretching func is_stretching() -> bool: return _is_stretching ##物理更新 func _physics_process(delta: float) -> void: if _is_stretching: _update_stretching(delta) ##更新绳索的动画表现 func _process(delta: float) -> void: update_line_target_pos_with_index(0,self.global_position) update_line_target_pos_with_index(1,area_2d.global_position) ##更新钩爪爪手的位置 func _update_stretching(delta: float) -> void: var d = self.global_position.distance_to(area_2d.global_position) #如果已经存在一个缓存的取消,且当前的长度小于最小长度,则直接取消 if _cached_cancel: if d >= min_length: stretching_finished.emit(true,null) end_stretching(true) return #如果达到的最大的长度,直接取消 if d > max_length: stretching_finished.emit(true,null) end_stretching(true) return area_2d.global_position += delta * streching_speed * _stretching_dir ## ================ ## Tool Func ## ================ ##更新特定点的位置 func update_line_target_pos_with_index(point_index: int, target_pos: Vector2) -> void: line_2d.set_point_position( point_index, target_pos - global_position ) ##当触碰到可以被抓握的Area后,自动在当前位置生成一个钩爪锚点,后续的移动交给其他系统 func grap_detected(node: Node2D) -> void: if node.is_in_group(GRAPABLE_GROUP): end_stretching(true) var d : float= self.global_position.distance_to(area_2d.global_position) var b = d == max_length var anchor := _create_anchor_on_node(node) if anchor: stretching_finished.emit(b,anchor) ##创建钩爪锚点 func _create_anchor_on_node(area: Node2D) -> Node2D: # 如果之前有锚点,先清掉 if _anchor and is_instance_valid(_anchor): _anchor.queue_free() # 1. 创建 Anchor _anchor = Node2D.new() _anchor.name = "Anchor" add_child(_anchor) # 2. Anchor 初始位置 = Area 当前世界位置 _anchor.global_position = area.global_position # 3. 在 Area 上挂 RemoteTransform2D var remote := RemoteTransform2D.new() remote.name = "AnchorRemote" area.add_child(remote) remote.remote_path = _anchor.get_path() # 4. 只同步位置(锚点一般不需要跟旋转/缩放) remote.update_rotation = false remote.update_scale = false return _anchor