From aee69ab5e8d62760ba599b2abf16b07da8f37b75 Mon Sep 17 00:00:00 2001 From: RedisTKey Date: Tue, 13 Jan 2026 00:52:36 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9B=B8=E6=9C=BA=E5=8A=9F=E8=83=BD=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _camera/CameraAnchor.tscn | 1 + _camera/PlateformerCamera.tscn | 5 + _game/GameMain.tscn | 5 +- _scene/level1/l1_s1.tscn | 1 - _scene/level1/l1_s2.tscn | 1 - addons/reedcamera/scripts/CameraAnchor.gd | 24 ++--- addons/reedcamera/scripts/CameraPointer.gd | 65 +++++++++-- addons/reedcamera/scripts/ReedCameraGlobal.gd | 25 ++++- .../camera_tools/CameraAnchorController.gd | 101 +++++++++++++----- .../camera_tools/CameraShakeController.gd | 8 +- .../scripts/camera_tools/CameraToolBasic.gd | 33 ++++++ .../camera_tools/CameraToolBasic.gd.uid | 1 + project.godot | 2 + 13 files changed, 204 insertions(+), 68 deletions(-) create mode 100644 addons/reedcamera/scripts/camera_tools/CameraToolBasic.gd create mode 100644 addons/reedcamera/scripts/camera_tools/CameraToolBasic.gd.uid diff --git a/_camera/CameraAnchor.tscn b/_camera/CameraAnchor.tscn index 97880f8..115dcce 100644 --- a/_camera/CameraAnchor.tscn +++ b/_camera/CameraAnchor.tscn @@ -4,3 +4,4 @@ [node name="CameraAnchor" type="Node2D"] script = ExtResource("1_1b11o") +zoom = Vector2(0.75, 0.75) diff --git a/_camera/PlateformerCamera.tscn b/_camera/PlateformerCamera.tscn index 1977e14..935cd8e 100644 --- a/_camera/PlateformerCamera.tscn +++ b/_camera/PlateformerCamera.tscn @@ -14,8 +14,13 @@ metadata/_custom_type_script = "uid://py4h5jxlncro" [node name="ReedCameraShakeController" type="Node" parent="CameraPointer"] script = ExtResource("2_rbequ") +affect_position = null +affect_offset = true +affect_zoom = null metadata/_custom_type_script = "uid://dwr1s51svvank" [node name="ReedCameraAnchorController" type="Node" parent="CameraPointer"] script = ExtResource("4_877nu") +affect_position = true +affect_zoom = true metadata/_custom_type_script = "uid://bhl5it46hv4n2" diff --git a/_game/GameMain.tscn b/_game/GameMain.tscn index 7476123..d732ca8 100644 --- a/_game/GameMain.tscn +++ b/_game/GameMain.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=12 format=3 uid="uid://3vc8ojbiyy5w"] +[gd_scene load_steps=13 format=3 uid="uid://3vc8ojbiyy5w"] [ext_resource type="Script" uid="uid://crgac4manhoud" path="res://_game/game.gd" id="1_yksyv"] [ext_resource type="PackedScene" uid="uid://cvqehvdjpoar4" path="res://_player/player_controller.tscn" id="2_x2i0j"] [ext_resource type="PackedScene" uid="uid://sursemsbf1lg" path="res://_scene/level1/l0_s0.tscn" id="3_4ifj7"] +[ext_resource type="PackedScene" uid="uid://cw6buluknvjj" path="res://_camera/PlateformerCamera.tscn" id="3_enubi"] [ext_resource type="PackedScene" uid="uid://cd88ydqhdo28" path="res://_scene/level1/l1_s1.tscn" id="4_m1t3p"] [ext_resource type="PackedScene" uid="uid://djs1eg5y008cs" path="res://_scene/level1/l1_s2.tscn" id="5_5s0xe"] [ext_resource type="PackedScene" uid="uid://dh43kt0l28qd5" path="res://_scene/level1/l1_s3.tscn" id="6_ktxjv"] @@ -17,6 +18,8 @@ script = ExtResource("1_yksyv") [node name="PlayerController" parent="." instance=ExtResource("2_x2i0j")] +[node name="PlateformerCamera" parent="." instance=ExtResource("3_enubi")] + [node name="L0_S0" parent="." instance=ExtResource("3_4ifj7")] [node name="L1_S1" parent="." instance=ExtResource("4_m1t3p")] diff --git a/_scene/level1/l1_s1.tscn b/_scene/level1/l1_s1.tscn index b9f4213..db6c7ee 100644 --- a/_scene/level1/l1_s1.tscn +++ b/_scene/level1/l1_s1.tscn @@ -119,7 +119,6 @@ script = ExtResource("7_hd3du") [node name="CameraAnchor" parent="Props" instance=ExtResource("8_vjpkl")] position = Vector2(-41, 0) -zoom = Vector2(0.75, 0.75) [node name="[Prop_0000]" type="Node" parent="Props/CameraAnchor"] script = ExtResource("9_ctwrc") diff --git a/_scene/level1/l1_s2.tscn b/_scene/level1/l1_s2.tscn index 503777a..47b0a66 100644 --- a/_scene/level1/l1_s2.tscn +++ b/_scene/level1/l1_s2.tscn @@ -140,7 +140,6 @@ limit_top = -335 limit_bottom = 240 limit_left = -427 limit_right = 427 -follow_player = true [node name="[Prop_0000]" type="Node" parent="Props/CameraAnchor"] script = ExtResource("9_fdfto") diff --git a/addons/reedcamera/scripts/CameraAnchor.gd b/addons/reedcamera/scripts/CameraAnchor.gd index 75f5acc..550bf75 100644 --- a/addons/reedcamera/scripts/CameraAnchor.gd +++ b/addons/reedcamera/scripts/CameraAnchor.gd @@ -63,16 +63,8 @@ const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") @export var limit_preview_color: Color = Color(0.9, 0.3, 0.3, 0.8) @export var limit_preview_line_width: float = 2.0 - -#@export_subgroup("Follow") -#@export var follow_player: bool = false - - - var _camera_global : Node = null - - var _priority: int : set(value): if _priority != value: @@ -85,15 +77,18 @@ func _ready() -> void: if not Engine.is_editor_hint(): _runtime_ready() +func _runtime_ready() -> void: + _priority = priority + func _enter_tree() -> void: if not Engine.is_editor_hint(): pass - #CameraSystem.register_anchor(self) + #_camera_global.register_anchor(self) #只有在runtime我们才向管理器注册anchor func _exit_tree() -> void: if not Engine.is_editor_hint(): pass - #CameraSystem.unregister_anchor(self) + #_camera_global.unregister_anchor(self) #只有在runtime我们才向管理器注册anchor func _draw() -> void: if not Engine.is_editor_hint(): @@ -144,8 +139,6 @@ func _camera_frame_rect_draw() -> Rect2: print("沒有找到全局相機") return Rect2(Vector2.ZERO,Vector2.ZERO) -func _runtime_ready() -> void: - _priority = priority ##内部懶加載全局相機管理器 func _get_camera_global() -> Object: @@ -157,9 +150,10 @@ func _get_camera_global() -> Object: return _camera_global +## 强制清空所有的camera anchor in register 然后把自己注册进去 func push_camera() -> void: - #CameraSystem.reset_all_camera_priority() - _priority = 1000 + _get_camera_global().clear_camera_anchors() + _get_camera_global().register_anchor(self) func pop_camera() -> void: - _priority = 0 + _get_camera_global().unregister_anchor(self) diff --git a/addons/reedcamera/scripts/CameraPointer.gd b/addons/reedcamera/scripts/CameraPointer.gd index e898c88..68d7ef1 100644 --- a/addons/reedcamera/scripts/CameraPointer.gd +++ b/addons/reedcamera/scripts/CameraPointer.gd @@ -10,7 +10,12 @@ var _runtime_registered := false var _warned := false var _final_offset := Vector2.ZERO -var _base_position: Vector2 +var _final_position: Vector2 +var _final_zoom := Vector2.ONE + +var _pos_dirty := false +var _offset_dirty := false +var _zoom_dirty := false func _enter_tree() -> void: if Engine.is_editor_hint(): @@ -29,31 +34,69 @@ func _process(delta: float) -> void: if not _camera: return - _update_base_position() + _reset_dirty_flags() + + _update_final_position() _update_camera_offset() - _apply_camera_transform() + _update_camera_zoom() + + _apply_camera_config() -func _update_base_position(): +func _reset_dirty_flags() -> void: + _pos_dirty = false + _offset_dirty = false + _zoom_dirty = false + +func _update_final_position(): for child in get_children(): - if child.has_method("get_base_position"): - _base_position = child.get_base_position() + if child is CameraToolBasic \ + and child.enabled \ + and child.affect_position \ + and child.has_base_position(): + _final_position = child.get_base_position() + _pos_dirty = true return # fallback - _base_position = _camera.global_position + _final_position = _camera.global_position func _update_camera_offset(): var offset := Vector2.ZERO + var used := false for child in get_children(): - if child.has_method("get_camera_offset"): + if child is CameraToolBasic \ + and child.enabled \ + and child.affect_offset \ + and child.has_camera_offset(): offset += child.get_camera_offset() + used = true _final_offset = offset + _offset_dirty = used -func _apply_camera_transform(): - _camera.global_position = _base_position - _camera.offset = _final_offset +func _update_camera_zoom(): + for child in get_children(): + if child is CameraToolBasic \ + and child.enabled \ + and child.affect_zoom \ + and child.has_camera_zoom(): + _final_zoom = child.get_camera_zoom() + _zoom_dirty = true + return + + # fallback + _final_zoom = _camera.zoom + +func _apply_camera_config(): + if _pos_dirty: + _camera.global_position = _final_position + + if _offset_dirty: + _camera.offset = _final_offset + + if _zoom_dirty: + _camera.zoom = _final_zoom func _notification(what: int) -> void: diff --git a/addons/reedcamera/scripts/ReedCameraGlobal.gd b/addons/reedcamera/scripts/ReedCameraGlobal.gd index 7433c67..3b1892b 100644 --- a/addons/reedcamera/scripts/ReedCameraGlobal.gd +++ b/addons/reedcamera/scripts/ReedCameraGlobal.gd @@ -9,8 +9,7 @@ var _camera_points : Array[CameraPointer] var _current_camera_point : CameraPointer -signal anchor_registered(anchor: CameraAnchor) -signal anchor_unregistered(anchor: CameraAnchor) +signal anchors_changed var _anchors : Array[CameraAnchor] @@ -50,16 +49,32 @@ func get_camera() -> Camera2D: func register_anchor(anchor: CameraAnchor) -> void: if anchor in _anchors: return + _anchors.append(anchor) - anchor_registered.emit(anchor) + + # 监听 anchor 内部变化 + anchor.on_priority_change.connect(_on_anchor_priority_changed) + + anchors_changed.emit() func unregister_anchor(anchor: CameraAnchor) -> void: - if _anchors.has(anchor): + if anchor in _anchors: _anchors.erase(anchor) - anchor_unregistered.emit(anchor) + + if anchor.on_priority_change.is_connected(_on_anchor_priority_changed): + anchor.on_priority_change.disconnect(_on_anchor_priority_changed) + + anchors_changed.emit() + +func _on_anchor_priority_changed(_p: int, _a: CameraAnchor) -> void: + anchors_changed.emit() func get_all_anchors() -> Array[CameraAnchor]: return _anchors.duplicate() + +func clear_camera_anchors() -> void: + _anchors.clear() + anchors_changed.emit() #endregion func _enter_tree() -> void: diff --git a/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd b/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd index 69c1050..ef1031b 100644 --- a/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd +++ b/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd @@ -1,4 +1,4 @@ -extends Node +extends CameraToolBasic class_name ReedCameraAnchorController const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") @@ -8,52 +8,65 @@ const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") var _anchors: Array[CameraAnchor] = [] var _current_anchor: CameraAnchor var _current_position := Vector2.ZERO +var _current_zoom := Vector2.ONE var _tween: Tween ## ========================= ## Config ## ========================= -@export var enabled := true @export var move_duration := 0.4 @export var ease_type := Tween.EASE_OUT @export var trans_type := Tween.TRANS_CUBIC -## ========================= -## Runtime State -## ========================= +var _evaluate_requested := false +var _evaluate_scheduled := false -#var _current_position: Vector2 -var _target_position: Vector2 -#var _tween: Tween -var _has_target := false +var _sys : Object = null + +enum AnchorState { + IDLE, + TRANSITIONING, +} + +var _state := AnchorState.IDLE ## ========================= ## Lifecycle ## ========================= func _ready() -> void: - var sys := Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME) - if not sys: + _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 - _anchors = sys.get_all_anchors() + _evaluate_scheduled = true + call_deferred("_commit_evaluate") - sys.anchor_registered.connect(_on_anchor_added) - sys.anchor_unregistered.connect(_on_anchor_removed) +func _commit_evaluate() -> void: + _evaluate_scheduled = false - _evaluate() + if not _evaluate_requested: + return -func _on_anchor_added(anchor: CameraAnchor) -> void: - _anchors.append(anchor) - _evaluate() + _evaluate_requested = false + _evaluate_impl() -func _on_anchor_removed(anchor: CameraAnchor) -> void: - _anchors.erase(anchor) - if _current_anchor == anchor: - _current_anchor = null - _evaluate() - -func _evaluate() -> void: +func _evaluate_impl() -> void: var winner := _pick_best_anchor() if winner == _current_anchor: return @@ -78,22 +91,56 @@ func _switch_to(anchor: CameraAnchor) -> void: 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_trans(Tween.TRANS_CUBIC) - _tween.set_ease(Tween.EASE_OUT) + _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 diff --git a/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd b/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd index 03fe761..1ee07eb 100644 --- a/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd +++ b/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd @@ -1,12 +1,6 @@ @tool -extends Node +extends CameraToolBasic class_name ReedCameraShakeController - -## ========================= -## Config -## ========================= -@export var enabled := true - ## ========================= ## Runtime ## ========================= diff --git a/addons/reedcamera/scripts/camera_tools/CameraToolBasic.gd b/addons/reedcamera/scripts/camera_tools/CameraToolBasic.gd new file mode 100644 index 0000000..84cf831 --- /dev/null +++ b/addons/reedcamera/scripts/camera_tools/CameraToolBasic.gd @@ -0,0 +1,33 @@ +@abstract +class_name CameraToolBasic +extends Node + +## ========================= +## Participation Flags +## ========================= +@export var enabled: bool = true + +@export var affect_position: bool = false +@export var affect_offset: bool = false +@export var affect_zoom: bool = false + +## ========================= +## Interfaces(子类选择性实现) +## ========================= +func get_base_position() -> Vector2: + return Vector2.ZERO + +func has_base_position() -> bool: + return false + +func get_camera_offset() -> Vector2: + return Vector2.ZERO + +func has_camera_offset() -> bool: + return false + +func get_camera_zoom() ->Vector2: + return Vector2.ONE + +func has_camera_zoom() -> bool: + return false diff --git a/addons/reedcamera/scripts/camera_tools/CameraToolBasic.gd.uid b/addons/reedcamera/scripts/camera_tools/CameraToolBasic.gd.uid new file mode 100644 index 0000000..9dcbb6f --- /dev/null +++ b/addons/reedcamera/scripts/camera_tools/CameraToolBasic.gd.uid @@ -0,0 +1 @@ +uid://bwj5j7hr5ucm3 diff --git a/project.godot b/project.godot index 00b590f..edb0817 100644 --- a/project.godot +++ b/project.godot @@ -24,6 +24,8 @@ ReedCameraSystem="*res://addons/reedcamera/scripts/ReedCameraGlobal.gd" [display] +window/size/viewport_width=640 +window/size/viewport_height=360 window/size/window_width_override=1920 window/size/window_height_override=1080 window/stretch/mode="canvas_items"