extends CameraToolBasic class_name ReedCameraAnchorController const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") ## ========================= ## Private ## ========================= var _anchors: Array[CameraAnchor] = [] var _current_anchor: CameraAnchor var _current_position := Vector2.ZERO var _current_zoom := Vector2.ONE var _tween: Tween ## ========================= ## Config ## ========================= @export var move_duration := 0.4 @export var ease_type := Tween.EASE_OUT @export var trans_type := Tween.TRANS_CUBIC var _evaluate_requested := false var _evaluate_scheduled := false var _sys : Object = null enum AnchorState { IDLE, TRANSITIONING, } var _state := AnchorState.IDLE ## ========================= ## Lifecycle ## ========================= func _ready() -> void: _anchors = _get_camera_system().get_all_anchors() _get_camera_system().anchors_changed.connect(_on_anchors_changed) _request_evaluate() func _on_anchors_changed() -> void: _sync_anchors() _request_evaluate() func _sync_anchors() -> void: _anchors = _get_camera_system().get_all_anchors() func _request_evaluate() -> void: _evaluate_requested = true if _evaluate_scheduled: return _evaluate_scheduled = true call_deferred("_commit_evaluate") func _commit_evaluate() -> void: _evaluate_scheduled = false if not _evaluate_requested: return _evaluate_requested = false _evaluate_impl() func _evaluate_impl() -> void: var winner := _pick_best_anchor() if winner == _current_anchor: return _switch_to(winner) func _pick_best_anchor() -> CameraAnchor: var best: CameraAnchor var best_p := -INF for a in _anchors: if not a.enabled: continue if a.priority > best_p: best = a best_p = a.priority return best func _switch_to(anchor: CameraAnchor) -> void: if not anchor: return _current_anchor = anchor _state = AnchorState.TRANSITIONING var target := anchor.global_position var target_zoom := anchor.zoom if (anchor.zoom.x > 0 and anchor.zoom.y > 0) else Vector2.ONE if _tween and _tween.is_running(): _tween.kill() if not anchor.use_blend or anchor.blend_time <= 0.0: _current_position = target _current_zoom = target_zoom _state = AnchorState.IDLE return _tween = get_tree().create_tween() _tween.set_ignore_time_scale(true) _tween.set_trans(trans_type) _tween.set_ease(ease_type) _tween.tween_property(self, "_current_position", target, anchor.blend_time) _tween.parallel().tween_property(self, "_current_zoom", target_zoom, anchor.blend_time) _tween.finished.connect(func(): _state = AnchorState.IDLE ) 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 ## ========================= ## CameraPointer Interface ## ========================= func get_base_position() -> Vector2: if _state != AnchorState.TRANSITIONING: return Vector2.ZERO return _current_position func has_base_position() -> bool: return _state == AnchorState.TRANSITIONING func get_camera_zoom() -> Vector2: if _state != AnchorState.TRANSITIONING: return Vector2.ONE return _current_zoom func has_camera_zoom() -> bool: return _state == AnchorState.TRANSITIONING