godot-plateformer/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd

147 lines
3.2 KiB
GDScript

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