extends CameraToolBasic class_name ReedCameraFollowController const _CONSTANTS := preload("res://addons/reedcamera/_data/CameraSystemConst.gd") const _DEBUG_TOOL := preload("res://addons/reedcamera/scripts/camera_tools/DeadZoneDebug.tscn") @export_group("Dead Zone") @export var dead_zone_ratio : Vector2 = Vector2(.6,.6) @export_group("Follow") @export var enabled_follow: bool = true @export var follow_speed: float = 600.0 # 世界单位 / 秒 @export var follow_lerp := 0.12 # 0~1,越大越“跟手”,越小越“蔚蓝感”的滞后 @export_subgroup("Follow Dynamic Speed") @export var min_speed_scale := 0.4 # 贴近死区时 @export var max_speed_scale := 2.0 # 远离死区时 @export var max_offset_ratio := 1.0 # offset 达到 1 个 dead_zone 时为最大速率 ##TODO:后续添加一下Runtime的修改逻辑 @export_group("Debug") @export var show_preview: bool = true var _show_preview: bool = false: set(value): _update_debug(value) _show_preview = value @export var preview_color: Color = Color.AQUAMARINE @export var preview_line_width : float = 2.0 enum State{ IDLE, CHASING, STATIC } ## 用于描述和切换follower工具的状态 var _current_state: State = State.IDLE ##Follower工具绑定的followNode var _follow_node2d : Node2D = null ##最终的位置 var _final_position: Vector2 ##缓存全局系统 var _sys: Object = null ##缓存DebugTool var _screen_ratio_rect_draw_tool: Node = null ##缓存camera var _camera: Camera2D = null func _ready() -> void: _show_preview = show_preview func _process(delta: float) -> void: #print(_current_state) if _current_state == State.IDLE: return if _current_state == State.CHASING or State.STATIC: update_follow(delta) func _update_debug(debug:bool) -> void: if debug: if _screen_ratio_rect_draw_tool: var t := _screen_ratio_rect_draw_tool.get_child(0) t.set_ratio(dead_zone_ratio) t.set_target_node(_follow_node2d) t.set_style(preview_color,preview_line_width) return else: var t := _DEBUG_TOOL.instantiate() self.add_child(t) _screen_ratio_rect_draw_tool = t t.get_child(0).set_ratio(dead_zone_ratio) t.get_child(0).set_target_node(_follow_node2d) t.get_child(0).set_style(preview_color,preview_line_width) else: if not _screen_ratio_rect_draw_tool: return else: _screen_ratio_rect_draw_tool.get_child(0).clear() func update_follow(delta: float) -> void: if not enabled_follow: return if not _follow_node2d: return var cam := _get_camera_from_pointer() if not cam: return # 0. 读取相机的全局坐标 _final_position = cam.global_position # 1. target 的屏幕坐标(Canvas Space) var t_in_screen :Vector2 = \ _follow_node2d.get_global_transform_with_canvas().get_origin() var screen_size : Vector2 = _get_camera_system().get_screen_size() var screen_center : Vector2 = screen_size * 0.5 var dead_half : Vector2 = screen_size * dead_zone_ratio * 0.5 # 2. 获得移动方向 var offset := \ _compute_deadzone_offset( t_in_screen, screen_center, dead_half ) if offset == Vector2.ZERO: _current_state = State.STATIC return _current_state = State.CHASING #var move_dir := offset.normalized() #var world_velocity := (move_dir * follow_speed) / cam.zoom var offset_len := offset.length() var dead_len := dead_half.length() # 0~1 的距离比例 var t := clamp(offset_len / (dead_len * max_offset_ratio), 0.0, 1.0) # 距离越远,速度越快 var speed_scale := lerp(min_speed_scale, max_speed_scale, t) var move_dir := offset.normalized() var world_velocity : Vector2 = (move_dir * follow_speed * speed_scale) / cam.zoom var desired_pos := _final_position + world_velocity * delta if follow_lerp > 0.0: _final_position = cam.global_position.lerp(desired_pos, follow_lerp) else: _final_position = desired_pos func _compute_deadzone_offset( screen_pos: Vector2, screen_center: Vector2, dead_half: Vector2 ) -> Vector2: var o := Vector2.ZERO var dx := screen_pos.x - screen_center.x var dy := screen_pos.y - screen_center.y if dx < -dead_half.x: o.x = dx + dead_half.x elif dx > dead_half.x: o.x = dx - dead_half.x if dy < -dead_half.y: o.y = dy + dead_half.y elif dy > dead_half.y: o.y = dy - dead_half.y return o ## 从相机指针获取相机组件 func _get_camera_from_pointer() -> Camera2D: if _camera: return _camera var p := get_parent() if p and p.has_method("get_camera"): _camera = p.get_camera() as Camera2D return _camera return _camera ## 获取全局相机管理器 func _get_camera_system() -> Object: if _sys: return _sys if Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME): _sys = Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME) return _sys ##外部调用,设置一个相机系统的跟随者 func register_follower(follower: Node2D) -> void: _follow_node2d = follower if self._current_state == State.IDLE: start_follow() ##外部调用,移除一共相机系统的跟随者 func unregister_follower() -> void: _follow_node2d = null stop_follow() ##外部调用,开始跟随 func start_follow() -> void: _current_state = State.STATIC ##外部调用,结束跟随 func stop_follow() -> void: _current_state = State.IDLE func get_base_position() -> Vector2: return _final_position func has_base_position() -> bool: return _current_state == State.CHASING