From 072688c9c141040bb2389fc9352ef83194f3d7e9 Mon Sep 17 00:00:00 2001 From: Reed Date: Mon, 12 Jan 2026 14:04:08 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E7=9B=B8=E6=A9=9F=E7=B3=BB=E7=B5=B1?= =?UTF-8?q?=E9=87=8D=E6=A7=8B=EF=BC=8C=E5=81=9A=E6=88=90=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=9A=84=E5=BD=A2=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {_shared/camera => _camera}/CameraAnchor.tscn | 5 +- _camera/CameraSystem.gd | 265 ++++++++++++++++++ .../camera => _camera}/CameraSystem.gd.uid | 0 {_shared/camera => _camera}/CameraSystem.tscn | 4 +- .../camera => _camera}/camera_follower.gd | 0 .../camera => _camera}/camera_follower.gd.uid | 0 .../camera => _camera}/camera_follower.tscn | 2 +- .../camera_shake/CSP_HorizontalOnly.tres | 2 +- .../camera_shake/CSP_VerticalOnly.tres | 2 +- .../camera_shake/CSP_XY.tres | 2 +- .../camera_shake/CameraShakePlayer.gd | 0 .../camera_shake/CameraShakePlayer.gd.uid | 0 .../camera_shake/CameraShakePreset.gd | 0 .../camera_shake/CameraShakePreset.gd.uid | 0 _player/Avatar.tscn | 8 +- .../_prefabs/player/player_respawn_point.gd | 1 + _scene/level1/l0_s0.tscn | 2 +- _scene/level1/l1_s1.tscn | 2 +- _scene/level1/l1_s2.tscn | 3 +- _scene/level1/l1_s3.tscn | 2 +- _scene/level1/l1_s4.tscn | 2 +- _scene/level1/l1_s5.tscn | 2 +- _scene/level1/l1_s6.tscn | 2 +- _scene/level1/l1_s7.tscn | 2 +- _scene/level1/l1_s8.tscn | 2 +- _scene/level1/l1_s9.tscn | 6 +- _shared/{global_event.gd => GlobalEvent.gd} | 1 + ...global_event.gd.uid => GlobalEvent.gd.uid} | 0 _shared/camera/CameraSystem.gd | 207 -------------- addons/reedcamera/_data/CameraSystemConst.gd | 14 + .../reedcamera/_data/CameraSystemConst.gd.uid | 1 + addons/reedcamera/plugin.cfg | 7 + addons/reedcamera/reedcamera.gd | 22 ++ addons/reedcamera/reedcamera.gd.uid | 1 + .../reedcamera/scripts}/CameraAnchor.gd | 65 +++-- .../reedcamera/scripts}/CameraAnchor.gd.uid | 0 addons/reedcamera/scripts/ReedCameraGlobal.gd | 34 +++ .../scripts/ReedCameraGlobal.gd.uid | 1 + project.godot | 12 +- 39 files changed, 418 insertions(+), 263 deletions(-) rename {_shared/camera => _camera}/CameraAnchor.tscn (61%) create mode 100644 _camera/CameraSystem.gd rename {_shared/camera => _camera}/CameraSystem.gd.uid (100%) rename {_shared/camera => _camera}/CameraSystem.tscn (82%) rename {_shared/camera => _camera}/camera_follower.gd (100%) rename {_shared/camera => _camera}/camera_follower.gd.uid (100%) rename {_shared/camera => _camera}/camera_follower.tscn (83%) rename {_shared/camera => _camera}/camera_shake/CSP_HorizontalOnly.tres (81%) rename {_shared/camera => _camera}/camera_shake/CSP_VerticalOnly.tres (81%) rename {_shared/camera => _camera}/camera_shake/CSP_XY.tres (81%) rename {_shared/camera => _camera}/camera_shake/CameraShakePlayer.gd (100%) rename {_shared/camera => _camera}/camera_shake/CameraShakePlayer.gd.uid (100%) rename {_shared/camera => _camera}/camera_shake/CameraShakePreset.gd (100%) rename {_shared/camera => _camera}/camera_shake/CameraShakePreset.gd.uid (100%) rename _shared/{global_event.gd => GlobalEvent.gd} (95%) rename _shared/{global_event.gd.uid => GlobalEvent.gd.uid} (100%) delete mode 100644 _shared/camera/CameraSystem.gd create mode 100644 addons/reedcamera/_data/CameraSystemConst.gd create mode 100644 addons/reedcamera/_data/CameraSystemConst.gd.uid create mode 100644 addons/reedcamera/plugin.cfg create mode 100644 addons/reedcamera/reedcamera.gd create mode 100644 addons/reedcamera/reedcamera.gd.uid rename {_shared/camera => addons/reedcamera/scripts}/CameraAnchor.gd (69%) rename {_shared/camera => addons/reedcamera/scripts}/CameraAnchor.gd.uid (100%) create mode 100644 addons/reedcamera/scripts/ReedCameraGlobal.gd create mode 100644 addons/reedcamera/scripts/ReedCameraGlobal.gd.uid diff --git a/_shared/camera/CameraAnchor.tscn b/_camera/CameraAnchor.tscn similarity index 61% rename from _shared/camera/CameraAnchor.tscn rename to _camera/CameraAnchor.tscn index 0b83572..97880f8 100644 --- a/_shared/camera/CameraAnchor.tscn +++ b/_camera/CameraAnchor.tscn @@ -1,7 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://bflwr7cryd2l0"] -[ext_resource type="Script" uid="uid://dyxrydokgy0qs" path="res://_shared/camera/CameraAnchor.gd" id="1_dhu66"] +[ext_resource type="Script" uid="uid://dyxrydokgy0qs" path="res://addons/reedcamera/scripts/CameraAnchor.gd" id="1_1b11o"] [node name="CameraAnchor" type="Node2D"] -script = ExtResource("1_dhu66") -use_camera_limit = true +script = ExtResource("1_1b11o") diff --git a/_camera/CameraSystem.gd b/_camera/CameraSystem.gd new file mode 100644 index 0000000..e958f8f --- /dev/null +++ b/_camera/CameraSystem.gd @@ -0,0 +1,265 @@ +#'''全局的相机管理器 + # + #======= 外部调用函数 ======= + # +#''' +#@tool +#extends Node +# +#@onready var camera_2d: Camera2D = %Camera2D +#@onready var camera_shake_player: CameraShakePlayer = %CameraShakePlayer +# +#var _cached_anchors: Array[CameraAnchor] = [] +#var _current_anchor: CameraAnchor +#var _switch_tween: Tween +#var _player_remote: RemoteTransform2D +#var _base_camera_pos := Vector2.ZERO +# +###标记位,用来检测当前帧是否存在相机切换 +#var _switch_scheduled := false +#var _dirty := false +#var _screen_size : Vector2i +# +### 玩家关卡内静态相机 +#const CAMERA_FOLLOWER:= preload("res://camera/camera_follower.tscn") +# +#func _ready() -> void: + ## Setting default screensize + #_screen_size = Vector2i( + #ProjectSettings.get_setting("display/window/size/viewport_width"), + #ProjectSettings.get_setting("display/window/size/viewport_height") + #) +# + ## For editor + #if Engine.is_editor_hint(): + #ProjectSettings.settings_changed.connect(func(): + #_screen_size = Vector2i( + #ProjectSettings.get_setting("display/window/size/viewport_width"), + #ProjectSettings.get_setting("display/window/size/viewport_height") + #) + #) + ## For runtime + #else: + #get_tree().get_root().size_changed.connect(func(): + #_screen_size = get_viewport().get_visible_rect().size + #) +# +#func _editor_ready() -> void: + #ProjectSettings.settings_changed.connect(func(): + #_screen_size = Vector2i( + #ProjectSettings.get_setting("display/window/size/viewport_width"), + #ProjectSettings.get_setting("display/window/size/viewport_height") + #) + #) +# +#func _runtime_ready() -> void: + #get_tree().get_root().size_changed.connect(func(): + #_screen_size = get_viewport().get_visible_rect().size + #) +# +##func _process(delta): + ##if not Engine.is_editor_hint(): + ##_runtime_process(delta) + # +# +#func _runtime_process(delta): + #if not camera_2d: + #return +# + #var shake_offset := camera_shake_player.update(delta) + #camera_2d.global_position = _base_camera_pos + shake_offset +# +#func _on_player_spawned(new_player: Player) -> void: + #if _current_anchor and _current_anchor.follow_player: + #_bind_camera_follow_to_player(new_player) +# +#func _on_player_dead() -> void: + #return +# +### 绑定camera 到 player +#func _bind_camera_follow_to_player(player: Player) -> void: + #if _player_remote and is_instance_valid(_player_remote): + #_player_remote.queue_free() + #_player_remote = null +# + #if not is_instance_valid(player): + #return +# + #var rt := RemoteTransform2D.new() + #rt.name = "__CameraAnchorFollow" + #rt.remote_path = camera_2d.get_path() + #rt.update_position = true + #rt.update_rotation = false + #rt.update_scale = false +# + #player.add_child(rt) + #_player_remote = rt +# +### 外部获取玩家全局相机 +#func get_cached_camera() -> Camera2D: + #return camera_2d +# +### 注册一个相机锚点 +#func register_anchor(anchor: CameraAnchor) -> void: + #if anchor in _cached_anchors: + #return + #_cached_anchors.append(anchor) + #anchor.on_priority_change.connect(on_anchor_priority_changed) + #_request_evaluate() +# +### 当相机锚点的权重改变时,向管理器触发事件 +#func on_anchor_priority_changed(priority:int, anchor: CameraAnchor) -> void: + #_request_evaluate() +# +### 注销一个相机锚点 +#func unregister_anchor(anchor: CameraAnchor) -> void: + #_cached_anchors.erase(anchor) + #if _current_anchor == anchor: + #_current_anchor = null + #_request_evaluate() +# +#func _request_evaluate() -> void: + #_dirty = true + #if _switch_scheduled: + #return + #_switch_scheduled = true + #call_deferred("_commit_camera_anchor") +# +#func _commit_camera_anchor() -> void: + #_switch_scheduled = false + #if not _dirty: + #return + #_dirty = false +# + ## 清理无效 + #_cached_anchors = _cached_anchors.filter(func(a): return is_instance_valid(a)) +# + #var winner: CameraAnchor = _pick_best_anchor() +# + #if winner == null: + #return +# + #if winner == _current_anchor: + #return +# + #switch_anchor(winner) +# +#func _pick_best_anchor() -> CameraAnchor: + #var best: CameraAnchor = null + #var best_p := -INF +# + #for a in _cached_anchors: + #if not a.enabled: + #continue + #if a._priority > best_p: + #best_p = a._priority + #best = a +# + #return best +# +### 排序已有的锚点 +#func _sort_anchors() -> void: + #_cached_anchors.sort_custom(func(a, b): + #return a._priority > b._priority + #) +# +### 尝试自切换 +#func _try_auto_switch() -> void: + #for a in _cached_anchors: + #if a.enabled: + #switch_anchor(a) + #_current_anchor = a + #return +# +# +# +###应用Anchor的Limit +#func _apply_anchor_limits(anchor: CameraAnchor) -> void: + #if not camera_2d: + #return + #camera_2d.limit_left = anchor.global_position.x + anchor.limit_left + #camera_2d.limit_right = anchor.global_position.x + anchor.limit_right + #camera_2d.limit_top = anchor.global_position.y + anchor.limit_top + #camera_2d.limit_bottom = anchor.global_position.y + anchor.limit_bottom +# +#func _clear_camera_limits() -> void: + #if not camera_2d: + #return + #camera_2d.limit_left = -10000000 + #camera_2d.limit_right = 10000000 + #camera_2d.limit_top = -10000000 + #camera_2d.limit_bottom = 10000000 +# +### 切换相机 +#func switch_anchor(target_anchor: CameraAnchor) -> void: + #if target_anchor == null: + #return + #if target_anchor == _current_anchor: + #return + #if not is_instance_valid(camera_2d): + #return +# + ##优先清除已有的remote + #if _player_remote: + #_player_remote.queue_free() + #_player_remote = null + # + ## 中断旧 Tween + #if _switch_tween and _switch_tween.is_running(): + #_switch_tween.kill() + #_switch_tween = null +# + #var camera := camera_2d + #var blend_time : float = max(target_anchor.blend_time, 0.001) +# + #_clear_camera_limits() + # + #_switch_tween = get_tree().create_tween() + #_switch_tween.set_ignore_time_scale(true) + #_switch_tween.set_trans(Tween.TRANS_CUBIC) + #_switch_tween.set_ease(Tween.EASE_OUT) +# + ## ===== 位置 ===== + #_switch_tween.tween_property( + #self, + #"_base_camera_pos", + #target_anchor.global_position, + #blend_time + #) +# + ## ===== Zoom ===== + #_switch_tween.parallel().tween_property( + #camera, + #"zoom", + #target_anchor.zoom, + #blend_time * 1.5 + #) +# + ## 完成回调 + #_switch_tween.finished.connect(func(): + #_current_anchor = target_anchor + #_update_anchor_follow_state(target_anchor) + #_apply_anchor_limits(target_anchor) + #) +# + #_current_anchor = target_anchor +# +#func _update_anchor_follow_state(anchor: CameraAnchor) -> void: + #if not anchor.follow_player: + #return +# + #var player := GlobalEvent.get_player() + #if player: + #_bind_camera_follow_to_player(player) +# +## ========================== +## External API +## ========================== +### 重置所有的Camera的_priority +#func reset_all_camera_priority() -> void: + #for a in _cached_anchors: + #a._priority = 0 +# +### 返回屏幕比例 +#func get_screen_size() -> Vector2i: + #return _screen_size diff --git a/_shared/camera/CameraSystem.gd.uid b/_camera/CameraSystem.gd.uid similarity index 100% rename from _shared/camera/CameraSystem.gd.uid rename to _camera/CameraSystem.gd.uid diff --git a/_shared/camera/CameraSystem.tscn b/_camera/CameraSystem.tscn similarity index 82% rename from _shared/camera/CameraSystem.tscn rename to _camera/CameraSystem.tscn index 71a47d2..027cfc8 100644 --- a/_shared/camera/CameraSystem.tscn +++ b/_camera/CameraSystem.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=3 format=3 uid="uid://b8pv5wtbo0y20"] -[ext_resource type="Script" uid="uid://04mchxkp161a" path="res://_shared/camera/CameraSystem.gd" id="1_xxnab"] -[ext_resource type="Script" uid="uid://sfrjes1hq8b7" path="res://_shared/camera/camera_shake/CameraShakePlayer.gd" id="2_xxnab"] +[ext_resource type="Script" uid="uid://04mchxkp161a" path="res://_camera/CameraSystem.gd" id="1_xxnab"] +[ext_resource type="Script" uid="uid://sfrjes1hq8b7" path="res://_camera/camera_shake/CameraShakePlayer.gd" id="2_xxnab"] [node name="CameraSystem" type="Node"] script = ExtResource("1_xxnab") diff --git a/_shared/camera/camera_follower.gd b/_camera/camera_follower.gd similarity index 100% rename from _shared/camera/camera_follower.gd rename to _camera/camera_follower.gd diff --git a/_shared/camera/camera_follower.gd.uid b/_camera/camera_follower.gd.uid similarity index 100% rename from _shared/camera/camera_follower.gd.uid rename to _camera/camera_follower.gd.uid diff --git a/_shared/camera/camera_follower.tscn b/_camera/camera_follower.tscn similarity index 83% rename from _shared/camera/camera_follower.tscn rename to _camera/camera_follower.tscn index 58d041a..80dc727 100644 --- a/_shared/camera/camera_follower.tscn +++ b/_camera/camera_follower.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://bmb1en8itine5"] -[ext_resource type="Script" uid="uid://1xtjkoufyjlp" path="res://_shared/camera/camera_follower.gd" id="1_8rvp5"] +[ext_resource type="Script" uid="uid://1xtjkoufyjlp" path="res://_camera/camera_follower.gd" id="1_8rvp5"] [node name="CameraFollower" type="RemoteTransform2D"] update_rotation = false diff --git a/_shared/camera/camera_shake/CSP_HorizontalOnly.tres b/_camera/camera_shake/CSP_HorizontalOnly.tres similarity index 81% rename from _shared/camera/camera_shake/CSP_HorizontalOnly.tres rename to _camera/camera_shake/CSP_HorizontalOnly.tres index e4b0897..df4df76 100644 --- a/_shared/camera/camera_shake/CSP_HorizontalOnly.tres +++ b/_camera/camera_shake/CSP_HorizontalOnly.tres @@ -1,6 +1,6 @@ [gd_resource type="Resource" script_class="CameraShakePreset" load_steps=2 format=3 uid="uid://iv3hfxqm5503"] -[ext_resource type="Script" uid="uid://btm85tbxvjmex" path="res://_shared/camera/camera_shake/CameraShakePreset.gd" id="1_mwfjy"] +[ext_resource type="Script" uid="uid://btm85tbxvjmex" path="res://_camera/camera_shake/CameraShakePreset.gd" id="1_mwfjy"] [resource] script = ExtResource("1_mwfjy") diff --git a/_shared/camera/camera_shake/CSP_VerticalOnly.tres b/_camera/camera_shake/CSP_VerticalOnly.tres similarity index 81% rename from _shared/camera/camera_shake/CSP_VerticalOnly.tres rename to _camera/camera_shake/CSP_VerticalOnly.tres index cc16794..573480c 100644 --- a/_shared/camera/camera_shake/CSP_VerticalOnly.tres +++ b/_camera/camera_shake/CSP_VerticalOnly.tres @@ -1,6 +1,6 @@ [gd_resource type="Resource" script_class="CameraShakePreset" load_steps=2 format=3 uid="uid://bs3cqsp23047i"] -[ext_resource type="Script" uid="uid://btm85tbxvjmex" path="res://_shared/camera/camera_shake/CameraShakePreset.gd" id="1_pnkiv"] +[ext_resource type="Script" uid="uid://btm85tbxvjmex" path="res://_camera/camera_shake/CameraShakePreset.gd" id="1_pnkiv"] [resource] script = ExtResource("1_pnkiv") diff --git a/_shared/camera/camera_shake/CSP_XY.tres b/_camera/camera_shake/CSP_XY.tres similarity index 81% rename from _shared/camera/camera_shake/CSP_XY.tres rename to _camera/camera_shake/CSP_XY.tres index 26ac07b..633d0ad 100644 --- a/_shared/camera/camera_shake/CSP_XY.tres +++ b/_camera/camera_shake/CSP_XY.tres @@ -1,6 +1,6 @@ [gd_resource type="Resource" script_class="CameraShakePreset" load_steps=2 format=3 uid="uid://cs50mkt830f8r"] -[ext_resource type="Script" uid="uid://btm85tbxvjmex" path="res://_shared/camera/camera_shake/CameraShakePreset.gd" id="1_3oq5o"] +[ext_resource type="Script" uid="uid://btm85tbxvjmex" path="res://_camera/camera_shake/CameraShakePreset.gd" id="1_3oq5o"] [resource] script = ExtResource("1_3oq5o") diff --git a/_shared/camera/camera_shake/CameraShakePlayer.gd b/_camera/camera_shake/CameraShakePlayer.gd similarity index 100% rename from _shared/camera/camera_shake/CameraShakePlayer.gd rename to _camera/camera_shake/CameraShakePlayer.gd diff --git a/_shared/camera/camera_shake/CameraShakePlayer.gd.uid b/_camera/camera_shake/CameraShakePlayer.gd.uid similarity index 100% rename from _shared/camera/camera_shake/CameraShakePlayer.gd.uid rename to _camera/camera_shake/CameraShakePlayer.gd.uid diff --git a/_shared/camera/camera_shake/CameraShakePreset.gd b/_camera/camera_shake/CameraShakePreset.gd similarity index 100% rename from _shared/camera/camera_shake/CameraShakePreset.gd rename to _camera/camera_shake/CameraShakePreset.gd diff --git a/_shared/camera/camera_shake/CameraShakePreset.gd.uid b/_camera/camera_shake/CameraShakePreset.gd.uid similarity index 100% rename from _shared/camera/camera_shake/CameraShakePreset.gd.uid rename to _camera/camera_shake/CameraShakePreset.gd.uid diff --git a/_player/Avatar.tscn b/_player/Avatar.tscn index a0b6477..46c64c7 100644 --- a/_player/Avatar.tscn +++ b/_player/Avatar.tscn @@ -5,12 +5,12 @@ [ext_resource type="Script" uid="uid://15n8yfyr4eqj" path="res://_player/states/grounded.gd" id="2_5p50s"] [ext_resource type="Texture2D" uid="uid://doxhsab56pe50" path="res://_asset/all.png" id="2_8nsdm"] [ext_resource type="Script" uid="uid://dcfq4wnx2g6bs" path="res://_player/player_locomotion.gd" id="2_11vl8"] -[ext_resource type="Script" uid="uid://btm85tbxvjmex" path="res://_shared/camera/camera_shake/CameraShakePreset.gd" id="2_u7cua"] -[ext_resource type="Resource" uid="uid://iv3hfxqm5503" path="res://_shared/camera/camera_shake/CSP_HorizontalOnly.tres" id="3_1a1t3"] -[ext_resource type="Resource" uid="uid://bs3cqsp23047i" path="res://_shared/camera/camera_shake/CSP_VerticalOnly.tres" id="4_01uoa"] +[ext_resource type="Script" uid="uid://btm85tbxvjmex" path="res://_camera/camera_shake/CameraShakePreset.gd" id="2_u7cua"] +[ext_resource type="Resource" uid="uid://iv3hfxqm5503" path="res://_camera/camera_shake/CSP_HorizontalOnly.tres" id="3_1a1t3"] +[ext_resource type="Resource" uid="uid://bs3cqsp23047i" path="res://_camera/camera_shake/CSP_VerticalOnly.tres" id="4_01uoa"] [ext_resource type="Script" uid="uid://b5hkfpjbye70" path="res://_player/states/idle.gd" id="4_30i7g"] [ext_resource type="BlackboardPlan" uid="uid://nlw7rxugv5uh" path="res://_player/bbp_player.tres" id="4_mwufa"] -[ext_resource type="Resource" uid="uid://cs50mkt830f8r" path="res://_shared/camera/camera_shake/CSP_XY.tres" id="5_ciuu3"] +[ext_resource type="Resource" uid="uid://cs50mkt830f8r" path="res://_camera/camera_shake/CSP_XY.tres" id="5_ciuu3"] [ext_resource type="Script" uid="uid://bpd54nf8oxwsb" path="res://_player/states/player_hsm.gd" id="6_8q4ov"] [ext_resource type="Script" uid="uid://po21boe8iqcc" path="res://_player/states/move.gd" id="7_rrwxs"] [ext_resource type="Script" uid="uid://cjf7kds0cipkw" path="res://_tools/limbo_state_helper.gd" id="8_clxy3"] diff --git a/_props/_prefabs/player/player_respawn_point.gd b/_props/_prefabs/player/player_respawn_point.gd index af1faa2..a712331 100644 --- a/_props/_prefabs/player/player_respawn_point.gd +++ b/_props/_prefabs/player/player_respawn_point.gd @@ -101,4 +101,5 @@ func respawn_avatar() -> CharacterBody2D: new_avatar.position = self.global_position get_tree().current_scene.add_child(new_avatar) + GlobalEvent.player_spawned.emit(new_avatar) return new_avatar diff --git a/_scene/level1/l0_s0.tscn b/_scene/level1/l0_s0.tscn index d229f61..aad64c3 100644 --- a/_scene/level1/l0_s0.tscn +++ b/_scene/level1/l0_s0.tscn @@ -9,7 +9,7 @@ [ext_resource type="Script" uid="uid://fxpk2ot6otfh" path="res://addons/reedscene/act/Act.gd" id="8_0m4o0"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="9_1oohu"] [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="10_g4f48"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="11_o5yb1"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="11_o5yb1"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="12_vhd7q"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="13_5fsuc"] [ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="14_rxcsc"] diff --git a/_scene/level1/l1_s1.tscn b/_scene/level1/l1_s1.tscn index 9ea1c52..b9f4213 100644 --- a/_scene/level1/l1_s1.tscn +++ b/_scene/level1/l1_s1.tscn @@ -9,7 +9,7 @@ [ext_resource type="Script" uid="uid://fxpk2ot6otfh" path="res://addons/reedscene/act/Act.gd" id="5_ubvm0"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="6_r0e2c"] [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="7_hd3du"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="8_vjpkl"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="8_vjpkl"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="9_ctwrc"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="10_0u6xi"] [ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="11_hatj6"] diff --git a/_scene/level1/l1_s2.tscn b/_scene/level1/l1_s2.tscn index a5a3f32..503777a 100644 --- a/_scene/level1/l1_s2.tscn +++ b/_scene/level1/l1_s2.tscn @@ -9,7 +9,7 @@ [ext_resource type="Resource" uid="uid://bym4pb0ellj7b" path="res://_scene/scene_trigger_resource/default_switch.tres" id="5_fdfto"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="6_agny0"] [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="7_8ou3l"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="8_dq7pn"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="8_dq7pn"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="9_fdfto"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="10_uerbs"] [ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="11_w12cs"] @@ -160,6 +160,7 @@ state_id = 1 effects = Array[ExtResource("12_fmhh5")]([ExtResource("14_nnp13")]) [node name="PlayerTriggerVolumn" parent="Props" instance=ExtResource("15_lg3ok")] +visible = false position = Vector2(560, -480) [node name="CollisionShape2D" type="CollisionShape2D" parent="Props/PlayerTriggerVolumn"] diff --git a/_scene/level1/l1_s3.tscn b/_scene/level1/l1_s3.tscn index 76ef885..60b1479 100644 --- a/_scene/level1/l1_s3.tscn +++ b/_scene/level1/l1_s3.tscn @@ -9,7 +9,7 @@ [ext_resource type="Script" uid="uid://fxpk2ot6otfh" path="res://addons/reedscene/act/Act.gd" id="5_vajwc"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="6_td1yf"] [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="7_pawhc"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="8_x0ktb"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="8_x0ktb"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="9_03jph"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="10_6tjqf"] [ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="11_vl8fy"] diff --git a/_scene/level1/l1_s4.tscn b/_scene/level1/l1_s4.tscn index 6bb9e70..df0f09b 100644 --- a/_scene/level1/l1_s4.tscn +++ b/_scene/level1/l1_s4.tscn @@ -9,7 +9,7 @@ [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="5_qtvqv"] [ext_resource type="Resource" uid="uid://bym4pb0ellj7b" path="res://_scene/scene_trigger_resource/default_switch.tres" id="5_x87uk"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="6_qtvqv"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="8_6bhoi"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="8_6bhoi"] [ext_resource type="TileSet" uid="uid://doepkfp83k0lb" path="res://_tileset/test.tres" id="8_wofhb"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="9_0dl6r"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="10_sv1n5"] diff --git a/_scene/level1/l1_s5.tscn b/_scene/level1/l1_s5.tscn index 0fa805a..e36160e 100644 --- a/_scene/level1/l1_s5.tscn +++ b/_scene/level1/l1_s5.tscn @@ -9,7 +9,7 @@ [ext_resource type="Script" uid="uid://fxpk2ot6otfh" path="res://addons/reedscene/act/Act.gd" id="7_c6mvs"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="8_ug4by"] [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="9_c10nq"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="10_xrx0u"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="10_xrx0u"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="11_q6hv2"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="12_3vve2"] [ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="13_fqjrf"] diff --git a/_scene/level1/l1_s6.tscn b/_scene/level1/l1_s6.tscn index 10e380f..d3a0f12 100644 --- a/_scene/level1/l1_s6.tscn +++ b/_scene/level1/l1_s6.tscn @@ -9,7 +9,7 @@ [ext_resource type="Script" uid="uid://fxpk2ot6otfh" path="res://addons/reedscene/act/Act.gd" id="7_u3uxu"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="8_n5ng2"] [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="9_v8ejv"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="10_fwa5x"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="10_fwa5x"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="11_mlg0v"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="12_3yfjx"] [ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="13_k40sn"] diff --git a/_scene/level1/l1_s7.tscn b/_scene/level1/l1_s7.tscn index 84fa3fc..e6afeb5 100644 --- a/_scene/level1/l1_s7.tscn +++ b/_scene/level1/l1_s7.tscn @@ -9,7 +9,7 @@ [ext_resource type="Script" uid="uid://fxpk2ot6otfh" path="res://addons/reedscene/act/Act.gd" id="7_u3uxu"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="8_n5ng2"] [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="9_v8ejv"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="10_fwa5x"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="10_fwa5x"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="11_mlg0v"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="12_3yfjx"] [ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="13_k40sn"] diff --git a/_scene/level1/l1_s8.tscn b/_scene/level1/l1_s8.tscn index fb2adc2..b8dd028 100644 --- a/_scene/level1/l1_s8.tscn +++ b/_scene/level1/l1_s8.tscn @@ -9,7 +9,7 @@ [ext_resource type="Script" uid="uid://fxpk2ot6otfh" path="res://addons/reedscene/act/Act.gd" id="7_o10qt"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="8_nkrpp"] [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="9_ru3iu"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="10_8v4hu"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="10_8v4hu"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="11_nkttg"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="12_7fah6"] [ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="13_ot6uu"] diff --git a/_scene/level1/l1_s9.tscn b/_scene/level1/l1_s9.tscn index 2f68abb..a6f00b5 100644 --- a/_scene/level1/l1_s9.tscn +++ b/_scene/level1/l1_s9.tscn @@ -3,13 +3,13 @@ [ext_resource type="Script" uid="uid://5e157vdk6175" path="res://addons/reedscene/scene/ReedScene.gd" id="1_7313i"] [ext_resource type="Script" uid="uid://bh066o84byplh" path="res://addons/reedscene/scene/ReedSceneID.gd" id="2_hxiiq"] [ext_resource type="Script" uid="uid://dn0ksjoswquf5" path="res://addons/reedscene/scene/SceneManager.gd" id="3_k2ysg"] -[ext_resource type="Script" uid="uid://ons77en82uls" path="res://addons/reedscene/scene/SceneTrigger.gd" id="4_ny1y7"] -[ext_resource type="Resource" uid="uid://gmaitie4ys4h" path="res://_shared/quick_scene_config/player_entered_act_1.tres" id="5_ri87v"] +[ext_resource type="Script" uid="uid://ons77en82uls" path="res://addons/reedscene/scene/scene_trigger/base/SceneTrigger.gd" id="4_ny1y7"] +[ext_resource type="Resource" path="res://_shared/quick_scene_config/player_entered_act_1.tres" id="5_ri87v"] [ext_resource type="Script" uid="uid://dsgl7lbyjsiif" path="res://addons/reedscene/act/ActManager.gd" id="6_esabr"] [ext_resource type="Script" uid="uid://fxpk2ot6otfh" path="res://addons/reedscene/act/Act.gd" id="7_2ws8l"] [ext_resource type="Script" uid="uid://baqgorvlumyju" path="res://addons/reedscene/act/SingleAct.gd" id="8_kk8q0"] [ext_resource type="Script" uid="uid://pxjf5vst08eo" path="res://addons/reedscene/prop/PropManager.gd" id="9_uiu2l"] -[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_shared/camera/CameraAnchor.tscn" id="10_npsqr"] +[ext_resource type="PackedScene" uid="uid://bflwr7cryd2l0" path="res://_camera/CameraAnchor.tscn" id="10_npsqr"] [ext_resource type="Script" uid="uid://b4menkyub4ce7" path="res://addons/reedscene/prop/PropComponent.gd" id="11_gkqbq"] [ext_resource type="Script" uid="uid://di41kt2tj34c2" path="res://addons/reedscene/prop/StateManager.gd" id="12_64ehn"] [ext_resource type="Script" uid="uid://7lml6d1t5xtq" path="res://addons/reedscene/prop/PropState.gd" id="13_a1xcn"] diff --git a/_shared/global_event.gd b/_shared/GlobalEvent.gd similarity index 95% rename from _shared/global_event.gd rename to _shared/GlobalEvent.gd index d018a9d..dfa9a06 100644 --- a/_shared/global_event.gd +++ b/_shared/GlobalEvent.gd @@ -1,5 +1,6 @@ extends Node +signal player_spawned(player: Player) signal player_dead(player: Player) var _cached_player_controller: PlayerController diff --git a/_shared/global_event.gd.uid b/_shared/GlobalEvent.gd.uid similarity index 100% rename from _shared/global_event.gd.uid rename to _shared/GlobalEvent.gd.uid diff --git a/_shared/camera/CameraSystem.gd b/_shared/camera/CameraSystem.gd deleted file mode 100644 index def8f48..0000000 --- a/_shared/camera/CameraSystem.gd +++ /dev/null @@ -1,207 +0,0 @@ -'''全局的相机管理器 - - ======= 外部调用函数 ======= - -''' -extends Node - -@onready var camera_2d: Camera2D = %Camera2D -@onready var camera_shake_player: CameraShakePlayer = %CameraShakePlayer - -var _cached_anchors: Array[CameraAnchor] = [] -var _current_anchor: CameraAnchor -var _switch_tween: Tween -var _player_remote: RemoteTransform2D -var _base_camera_pos := Vector2.ZERO - -##标记位,用来检测当前帧是否存在相机切换 -var _switch_scheduled := false -var _dirty := false - -## 玩家关卡内静态相机 -const CAMERA_FOLLOWER:= preload("res://_shared/camera/camera_follower.tscn") - -func _ready() -> void: - _base_camera_pos = camera_2d.global_position - -func _process(delta): - if not camera_2d: - return - - var shake_offset := camera_shake_player.update(delta) - camera_2d.global_position = _base_camera_pos + shake_offset - -## 外部获取玩家全局相机 -func get_cached_camera() -> Camera2D: - return camera_2d - -## 注册一个相机锚点 -func register_anchor(anchor: CameraAnchor) -> void: - if anchor in _cached_anchors: - return - _cached_anchors.append(anchor) - anchor.on_priority_change.connect(on_anchor_priority_changed) - _request_evaluate() - -## 当相机锚点的权重改变时,向管理器触发事件 -func on_anchor_priority_changed(priority:int, anchor: CameraAnchor) -> void: - _request_evaluate() - -## 注销一个相机锚点 -func unregister_anchor(anchor: CameraAnchor) -> void: - _cached_anchors.erase(anchor) - if _current_anchor == anchor: - _current_anchor = null - _request_evaluate() - -func _request_evaluate() -> void: - _dirty = true - if _switch_scheduled: - return - _switch_scheduled = true - call_deferred("_commit_camera_anchor") - -func _commit_camera_anchor() -> void: - _switch_scheduled = false - if not _dirty: - return - _dirty = false - - # 清理无效 - _cached_anchors = _cached_anchors.filter(func(a): return is_instance_valid(a)) - - var winner: CameraAnchor = _pick_best_anchor() - - if winner == null: - return - - if winner == _current_anchor: - return - - switch_anchor(winner) - -func _pick_best_anchor() -> CameraAnchor: - var best: CameraAnchor = null - var best_p := -INF - - for a in _cached_anchors: - if not a.enabled: - continue - if a._priority > best_p: - best_p = a._priority - best = a - - return best - -## 排序已有的锚点 -func _sort_anchors() -> void: - _cached_anchors.sort_custom(func(a, b): - return a._priority > b._priority - ) - -## 尝试自切换 -func _try_auto_switch() -> void: - for a in _cached_anchors: - if a.enabled: - switch_anchor(a) - _current_anchor = a - return - -## 重置所有的Camera的_priority -func reset_all_camera_priority() -> void: - for a in _cached_anchors: - a._priority = 0 - -##应用Anchor的Limit -func _apply_anchor_limits(anchor: CameraAnchor) -> void: - if not camera_2d: - return - camera_2d.limit_left = anchor.global_position.x + anchor.limit_left - camera_2d.limit_right = anchor.global_position.x + anchor.limit_right - camera_2d.limit_top = anchor.global_position.y + anchor.limit_top - camera_2d.limit_bottom = anchor.global_position.y + anchor.limit_bottom - -func _clear_camera_limits() -> void: - if not camera_2d: - return - camera_2d.limit_left = -10000000 - camera_2d.limit_right = 10000000 - camera_2d.limit_top = -10000000 - camera_2d.limit_bottom = 10000000 - -## 切换相机 -func switch_anchor(target_anchor: CameraAnchor) -> void: - if target_anchor == null: - return - if target_anchor == _current_anchor: - return - if not is_instance_valid(camera_2d): - return - - #优先清除已有的remote - if _player_remote: - _player_remote.queue_free() - _player_remote = null - - # 中断旧 Tween - if _switch_tween and _switch_tween.is_running(): - _switch_tween.kill() - _switch_tween = null - - var camera := camera_2d - var blend_time : float = max(target_anchor.blend_time, 0.001) - - _clear_camera_limits() - - _switch_tween = get_tree().create_tween() - _switch_tween.set_ignore_time_scale(true) - _switch_tween.set_trans(Tween.TRANS_CUBIC) - _switch_tween.set_ease(Tween.EASE_OUT) - - # ===== 位置 ===== - _switch_tween.tween_property( - self, - "_base_camera_pos", - target_anchor.global_position, - blend_time - ) - - # ===== Zoom ===== - _switch_tween.parallel().tween_property( - camera, - "zoom", - target_anchor.zoom, - blend_time * 1.5 - ) - - # 完成回调 - _switch_tween.finished.connect(func(): - _current_anchor = target_anchor - _update_anchor_follow_state(target_anchor) - _apply_anchor_limits(target_anchor) - ) - - _current_anchor = target_anchor - -func _update_anchor_follow_state(anchor: CameraAnchor) -> void: - # 先清理旧的 RemoteTransform - if _player_remote and is_instance_valid(_player_remote): - _player_remote.queue_free() - _player_remote = null - - if not anchor.follow_player: - return - - var p := GlobalEvent.get_player() - - if not p or not is_instance_valid(p): - push_warning("CameraAnchor wants to follow player, but player is not registered.") - return - - # 创建 RemoteTransform2D - var rt := RemoteTransform2D.new() - rt.name = "__CameraAnchorFollow" - rt.remote_path = camera_2d.get_path() - - p.add_child(rt) - _player_remote = rt diff --git a/addons/reedcamera/_data/CameraSystemConst.gd b/addons/reedcamera/_data/CameraSystemConst.gd new file mode 100644 index 0000000..88a49d5 --- /dev/null +++ b/addons/reedcamera/_data/CameraSystemConst.gd @@ -0,0 +1,14 @@ +@tool +extends RefCounted + +#region Constants + +const CAMERA_SYSTEM_NAME : StringName = "ReedCameraSystem" +const CAMERA_ANCHOR_NAME : StringName = "CameraAnchor" +const CAMERA_FOLLOWER_NAME : StringName = "CameraFollower" + +#endregion + +#region GroupName + +#endregion diff --git a/addons/reedcamera/_data/CameraSystemConst.gd.uid b/addons/reedcamera/_data/CameraSystemConst.gd.uid new file mode 100644 index 0000000..522ab59 --- /dev/null +++ b/addons/reedcamera/_data/CameraSystemConst.gd.uid @@ -0,0 +1 @@ +uid://bpsj1nyihan67 diff --git a/addons/reedcamera/plugin.cfg b/addons/reedcamera/plugin.cfg new file mode 100644 index 0000000..93892c6 --- /dev/null +++ b/addons/reedcamera/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="ReedCamera" +description="Reed zhu 製作的用於平臺跳躍類游戲的簡易Camera2D插件" +author="ReedZhu" +version="" +script="reedcamera.gd" diff --git a/addons/reedcamera/reedcamera.gd b/addons/reedcamera/reedcamera.gd new file mode 100644 index 0000000..3cf469b --- /dev/null +++ b/addons/reedcamera/reedcamera.gd @@ -0,0 +1,22 @@ +@tool +extends EditorPlugin + +const REED_CAMERA_SYSTEM: StringName = "ReedCameraSystem" + +func _enable_plugin() -> void: + if not Engine.has_singleton(REED_CAMERA_SYSTEM): + add_autoload_singleton(REED_CAMERA_SYSTEM, "res://addons/reedcamera/scripts/ReedCameraGlobal.gd") + + +func _disable_plugin() -> void: + remove_autoload_singleton(REED_CAMERA_SYSTEM) + + +func _enter_tree() -> void: + # Initialization of the plugin goes here. + pass + + +func _exit_tree() -> void: + # Clean-up of the plugin goes here. + pass diff --git a/addons/reedcamera/reedcamera.gd.uid b/addons/reedcamera/reedcamera.gd.uid new file mode 100644 index 0000000..24a4175 --- /dev/null +++ b/addons/reedcamera/reedcamera.gd.uid @@ -0,0 +1 @@ +uid://dqmq1t1dmsy2 diff --git a/_shared/camera/CameraAnchor.gd b/addons/reedcamera/scripts/CameraAnchor.gd similarity index 69% rename from _shared/camera/CameraAnchor.gd rename to addons/reedcamera/scripts/CameraAnchor.gd index e254039..96f4dee 100644 --- a/_shared/camera/CameraAnchor.gd +++ b/addons/reedcamera/scripts/CameraAnchor.gd @@ -1,7 +1,10 @@ +##TODO:清楚掉這裏和PhantomCamera相關的部分 @tool @icon("uid://bsdmq0essfmpk") class_name CameraAnchor extends Node2D +const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") + @export_group("Anchor Config") ##该priority不会直接修改Anchor的priority,只用作初始化 @export var priority: int = 0 @@ -51,7 +54,7 @@ class_name CameraAnchor extends Node2D -var _pcam_manager: Node = null +var _camera_global : Node = null ## 编辑器预览面板设置 @export_group("Editor Preview") @@ -76,25 +79,22 @@ func _ready() -> void: _runtime_ready() func _enter_tree() -> void: - if Engine.is_editor_hint(): - _pcam_manager = Engine.get_singleton("PhantomCameraManager") - return - - CameraSystem.register_anchor(self) + if not Engine.is_editor_hint(): + pass + #CameraSystem.register_anchor(self) func _exit_tree() -> void: - if Engine.is_editor_hint(): - _pcam_manager = null - return - CameraSystem.unregister_anchor(self) + if not Engine.is_editor_hint(): + pass + #CameraSystem.unregister_anchor(self) func _draw() -> void: if not Engine.is_editor_hint(): return - if show_camera_preview and is_instance_valid(_pcam_manager): + if show_camera_preview: draw_rect( - _camera_frame_rect_like_phantom(), + _camera_frame_rect_draw(), preview_color, false, preview_line_width @@ -117,27 +117,42 @@ func _camera_limit_rect() -> Rect2: return Rect2(pos, size) -func _camera_frame_rect_like_phantom() -> Rect2: - # PhantomCamera 使用的是 manager.screen_size - var screen_size: Vector2 = _pcam_manager.screen_size +func _camera_frame_rect_draw() -> Rect2: + var cg = _get_camera_global() + if cg: + var screen_size: Vector2 = cg.get_screen_size() - var z := zoom - z.x = maxf(z.x, 0.001) - z.y = maxf(z.y, 0.001) + var z := zoom + z.x = maxf(z.x, 0.001) + z.y = maxf(z.y, 0.001) - var size := Vector2( - screen_size.x / z.x, - screen_size.y / z.y - ) + var size := Vector2( + screen_size.x / z.x, + screen_size.y / z.y + ) - # PhantomCamera:以自身为中心 - return Rect2(-size * 0.5, size) + # PhantomCamera:以自身为中心 + return Rect2(-size * 0.5, size) + + print("沒有找到全局相機") + return Rect2(Vector2.ZERO,Vector2.ZERO) func _runtime_ready() -> void: _priority = priority +##内部懶加載全局相機管理器 +func _get_camera_global() -> Object: + if _camera_global: + return _camera_global + + if Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME): + _camera_global = Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME) + print("") + + return _camera_global + func push_camera() -> void: - CameraSystem.reset_all_camera_priority() + #CameraSystem.reset_all_camera_priority() _priority = 1000 func pop_camera() -> void: diff --git a/_shared/camera/CameraAnchor.gd.uid b/addons/reedcamera/scripts/CameraAnchor.gd.uid similarity index 100% rename from _shared/camera/CameraAnchor.gd.uid rename to addons/reedcamera/scripts/CameraAnchor.gd.uid diff --git a/addons/reedcamera/scripts/ReedCameraGlobal.gd b/addons/reedcamera/scripts/ReedCameraGlobal.gd new file mode 100644 index 0000000..5b217d1 --- /dev/null +++ b/addons/reedcamera/scripts/ReedCameraGlobal.gd @@ -0,0 +1,34 @@ +@tool +extends Node + +const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") + +var _screen_size : Vector2i + +func _enter_tree() -> void: + if not Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME): + Engine.register_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME, self) + +func _ready() -> void: + # Setting default screensize + _screen_size = Vector2i( + ProjectSettings.get_setting("display/window/size/viewport_width"), + ProjectSettings.get_setting("display/window/size/viewport_height") + ) + + # For editor + if Engine.is_editor_hint(): + ProjectSettings.settings_changed.connect(func(): + _screen_size = Vector2i( + ProjectSettings.get_setting("display/window/size/viewport_width"), + ProjectSettings.get_setting("display/window/size/viewport_height") + ) + ) + # For runtime + else: + get_tree().get_root().size_changed.connect(func(): + _screen_size = get_viewport().get_visible_rect().size + ) + +func get_screen_size() -> Vector2i: + return _screen_size diff --git a/addons/reedcamera/scripts/ReedCameraGlobal.gd.uid b/addons/reedcamera/scripts/ReedCameraGlobal.gd.uid new file mode 100644 index 0000000..fd68bd2 --- /dev/null +++ b/addons/reedcamera/scripts/ReedCameraGlobal.gd.uid @@ -0,0 +1 @@ +uid://dqtfis2553nhn diff --git a/project.godot b/project.godot index 53652a9..79fd9e2 100644 --- a/project.godot +++ b/project.godot @@ -10,15 +10,16 @@ config_version=5 [application] -config/name="PhantomAgnes" +config/name="godot-plateformer" run/main_scene="uid://3vc8ojbiyy5w" config/features=PackedStringArray("4.5", "Forward Plus") config/icon="res://icon.svg" [autoload] -CameraSystem="*res://_shared/camera/CameraSystem.tscn" -GlobalEvent="*res://_shared/global_event.gd" +ReedCameraSystem="*res://addons/reedcamera/scripts/ReedCameraGlobal.gd" +CameraSystem="*res://_camera/CameraSystem.tscn" +GlobalEvent="*res://_shared/GlobalEvent.gd" ReedVFX="*res://addons/reedfx/vfx/ReedVFXSystem.tscn" ReedSceneRegistry="*res://addons/reedscene/scene/SceneRegistry.gd" @@ -32,7 +33,7 @@ window/stretch/mode="canvas_items" [editor_plugins] -enabled=PackedStringArray("res://addons/reedcomponent/plugin.cfg", "res://addons/reedfx/plugin.cfg", "res://addons/reedinput/plugin.cfg", "res://addons/reedscene/plugin.cfg") +enabled=PackedStringArray("res://addons/reedcamera/plugin.cfg", "res://addons/reedcomponent/plugin.cfg", "res://addons/reedfx/plugin.cfg", "res://addons/reedinput/plugin.cfg", "res://addons/reedscene/plugin.cfg") [file_customization] @@ -42,9 +43,8 @@ folder_colors={ [global_group] -ROOM="房间分组,其下存在所有的Room" PLAYER="玩家分组,其下只存在玩家控制器" -GRAPABLE="" +GRAPABLE="進入該分組的Collision視爲可以被玩家抓握" PLAYER_RESPAWN="所有的PlayerRespawnPoint的绑定Group" [input] From 98178a2bd23768570007cc741a44e5c334a26ccd Mon Sep 17 00:00:00 2001 From: Reed Date: Mon, 12 Jan 2026 17:51:15 +0800 Subject: [PATCH 2/5] WIP: refactor camera anchor system --- _camera/CameraSystem.gd | 2 +- _camera/PlateformerCamera.tscn | 21 +++ _camera/camera_follower.gd | 11 +- _camera/camera_test.gd | 8 ++ _camera/camera_test.gd.uid | 1 + _player/Avatar.tscn | 1 + _player/spider_avatar.tscn | 33 +++++ _player/spider_avatar.tscn827187919.tmp | 30 +++++ _player/states/grapping.gd | 4 +- addons/reedcamera/_example/test.tscn | 14 ++ addons/reedcamera/_example/test_shake.tres | 9 ++ .../reedcamera}/icon/camera_anchor_icon.svg | 0 .../icon/camera_anchor_icon.svg.import | 6 +- .../ref_counted/ReedCameraShakePlayer.gd | 55 ++++++++ .../ref_counted/ReedCameraShakePlayer.gd.uid | 1 + .../resource/ReedCameraShakePreset.gd | 9 ++ .../resource/ReedCameraShakePreset.gd.uid | 1 + addons/reedcamera/scripts/CameraAnchor.gd | 22 +-- addons/reedcamera/scripts/CameraPointer.gd | 125 ++++++++++++++++++ .../reedcamera/scripts/CameraPointer.gd.uid | 1 + addons/reedcamera/scripts/ReedCameraGlobal.gd | 57 ++++++++ .../camera_tools/CameraAnchorController.gd | 99 ++++++++++++++ .../CameraAnchorController.gd.uid | 1 + .../camera_tools/CameraShakeController.gd | 66 +++++++++ .../camera_tools/CameraShakeController.gd.uid | 1 + project.godot | 5 +- 26 files changed, 560 insertions(+), 23 deletions(-) create mode 100644 _camera/PlateformerCamera.tscn create mode 100644 _camera/camera_test.gd create mode 100644 _camera/camera_test.gd.uid create mode 100644 _player/spider_avatar.tscn create mode 100644 _player/spider_avatar.tscn827187919.tmp create mode 100644 addons/reedcamera/_example/test.tscn create mode 100644 addons/reedcamera/_example/test_shake.tres rename {_asset => addons/reedcamera}/icon/camera_anchor_icon.svg (100%) rename {_asset => addons/reedcamera}/icon/camera_anchor_icon.svg.import (76%) create mode 100644 addons/reedcamera/ref_counted/ReedCameraShakePlayer.gd create mode 100644 addons/reedcamera/ref_counted/ReedCameraShakePlayer.gd.uid create mode 100644 addons/reedcamera/resource/ReedCameraShakePreset.gd create mode 100644 addons/reedcamera/resource/ReedCameraShakePreset.gd.uid create mode 100644 addons/reedcamera/scripts/CameraPointer.gd create mode 100644 addons/reedcamera/scripts/CameraPointer.gd.uid create mode 100644 addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd create mode 100644 addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd.uid create mode 100644 addons/reedcamera/scripts/camera_tools/CameraShakeController.gd create mode 100644 addons/reedcamera/scripts/camera_tools/CameraShakeController.gd.uid diff --git a/_camera/CameraSystem.gd b/_camera/CameraSystem.gd index e958f8f..19cbb78 100644 --- a/_camera/CameraSystem.gd +++ b/_camera/CameraSystem.gd @@ -4,7 +4,7 @@ # #''' #@tool -#extends Node +extends Node # #@onready var camera_2d: Camera2D = %Camera2D #@onready var camera_shake_player: CameraShakePlayer = %CameraShakePlayer diff --git a/_camera/PlateformerCamera.tscn b/_camera/PlateformerCamera.tscn new file mode 100644 index 0000000..1977e14 --- /dev/null +++ b/_camera/PlateformerCamera.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=5 format=3 uid="uid://cw6buluknvjj"] + +[ext_resource type="Script" uid="uid://djk7tg2puphgv" path="res://_camera/camera_test.gd" id="1_05blt"] +[ext_resource type="Script" uid="uid://py4h5jxlncro" path="res://addons/reedcamera/scripts/CameraPointer.gd" id="1_e7rkk"] +[ext_resource type="Script" uid="uid://dwr1s51svvank" path="res://addons/reedcamera/scripts/camera_tools/CameraShakeController.gd" id="2_rbequ"] +[ext_resource type="Script" uid="uid://bhl5it46hv4n2" path="res://addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd" id="4_877nu"] + +[node name="PlateformerCamera" type="Camera2D"] +script = ExtResource("1_05blt") + +[node name="CameraPointer" type="Node" parent="."] +script = ExtResource("1_e7rkk") +metadata/_custom_type_script = "uid://py4h5jxlncro" + +[node name="ReedCameraShakeController" type="Node" parent="CameraPointer"] +script = ExtResource("2_rbequ") +metadata/_custom_type_script = "uid://dwr1s51svvank" + +[node name="ReedCameraAnchorController" type="Node" parent="CameraPointer"] +script = ExtResource("4_877nu") +metadata/_custom_type_script = "uid://bhl5it46hv4n2" diff --git a/_camera/camera_follower.gd b/_camera/camera_follower.gd index 8a96188..7592dc8 100644 --- a/_camera/camera_follower.gd +++ b/_camera/camera_follower.gd @@ -1,8 +1,9 @@ extends RemoteTransform2D func _ready() -> void: - var global_camera: Camera2D = CameraSystem.get_cached_camera() - if not global_camera: - push_error("[CameraFollower]:No Global Camera Founded") - return - remote_path = global_camera.get_path() + pass + #var global_camera: Camera2D = CameraSystem.get_cached_camera() + #if not global_camera: + #push_error("[CameraFollower]:No Global Camera Founded") + #return + #remote_path = global_camera.get_path() diff --git a/_camera/camera_test.gd b/_camera/camera_test.gd new file mode 100644 index 0000000..798525e --- /dev/null +++ b/_camera/camera_test.gd @@ -0,0 +1,8 @@ +extends Camera2D + + +func _unhandled_input(event: InputEvent) -> void: + if event.is_action_pressed("ui_accept"): + $CameraPointer/ReedCameraShakeController.play_shake(preload("res://addons/reedcamera/_example/test_shake.tres")) + #elif event.is_action_pressed("ui_right"): + #self.global_position += Vector2(100,0) diff --git a/_camera/camera_test.gd.uid b/_camera/camera_test.gd.uid new file mode 100644 index 0000000..0d55b26 --- /dev/null +++ b/_camera/camera_test.gd.uid @@ -0,0 +1 @@ +uid://djk7tg2puphgv diff --git a/_player/Avatar.tscn b/_player/Avatar.tscn index 46c64c7..16210e9 100644 --- a/_player/Avatar.tscn +++ b/_player/Avatar.tscn @@ -78,6 +78,7 @@ unique_name_in_owner = true shape = SubResource("RectangleShape2D_qnulu") [node name="Sprite2D" type="Sprite2D" parent="."] +visible = false texture_filter = 1 position = Vector2(0, -2) texture = SubResource("AtlasTexture_basl5") diff --git a/_player/spider_avatar.tscn b/_player/spider_avatar.tscn new file mode 100644 index 0000000..fe7ff37 --- /dev/null +++ b/_player/spider_avatar.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=5 format=3 uid="uid://ck8m7revy150r"] + +[ext_resource type="PackedScene" uid="uid://gwhff4qaouxy" path="res://_player/Avatar.tscn" id="1_irquc"] +[ext_resource type="Texture2D" uid="uid://dted7geb331y2" path="res://_asset/ksw/character.png" id="2_350jv"] + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_350jv"] +radius = 41.0 +height = 134.0 + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_e1o4i"] +radius = 45.0 +height = 145.0 + +[node name="Avatar" instance=ExtResource("1_irquc")] + +[node name="CollisionShape2D" parent="." index="1"] +visible = false +shape = SubResource("CapsuleShape2D_350jv") + +[node name="Sprite2D" parent="." index="2"] +visible = true +texture_filter = 0 +texture = ExtResource("2_350jv") + +[node name="LocomotionComponent" parent="." index="5"] +_can_move = false + +[node name="CollisionShape2D" parent="HitBox" index="0"] +visible = false +position = Vector2(0, 0) +shape = SubResource("CapsuleShape2D_e1o4i") + +[editable path="LocomotionComponent/WallDetector"] diff --git a/_player/spider_avatar.tscn827187919.tmp b/_player/spider_avatar.tscn827187919.tmp new file mode 100644 index 0000000..d020fe6 --- /dev/null +++ b/_player/spider_avatar.tscn827187919.tmp @@ -0,0 +1,30 @@ +[gd_scene load_steps=5 format=3 uid="uid://ck8m7revy150r"] + +[ext_resource type="PackedScene" uid="uid://gwhff4qaouxy" path="res://_player/Avatar.tscn" id="1_irquc"] +[ext_resource type="Texture2D" uid="uid://dted7geb331y2" path="res://_asset/ksw/character.png" id="2_350jv"] + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_350jv"] +radius = 41.0 +height = 134.0 + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_e1o4i"] +radius = 45.0 +height = 145.0 + +[node name="Avatar" instance=ExtResource("1_irquc")] + +[node name="CollisionShape2D" parent="." index="1"] +visible = false +shape = SubResource("CapsuleShape2D_350jv") + +[node name="Sprite2D" parent="." index="2"] +visible = true +texture_filter = 0 +texture = ExtResource("2_350jv") + +[node name="CollisionShape2D" parent="HitBox" index="0"] +visible = false +position = Vector2(0, 0) +shape = SubResource("CapsuleShape2D_e1o4i") + +[editable path="LocomotionComponent/WallDetector"] diff --git a/_player/states/grapping.gd b/_player/states/grapping.gd index a02035d..11ac4f7 100644 --- a/_player/states/grapping.gd +++ b/_player/states/grapping.gd @@ -44,8 +44,8 @@ func _enter() -> void: elif i == 3 or i == 7: csp = agent.camera_shake_preset.get("xy_light") - if csp: - CameraSystem.camera_shake_player.play(csp) + #if csp: + #CameraSystem.camera_shake_player.play(csp) if root.grap_hook_state._jump_grace_timer > 0: _hook_to_jump() diff --git a/addons/reedcamera/_example/test.tscn b/addons/reedcamera/_example/test.tscn new file mode 100644 index 0000000..0042307 --- /dev/null +++ b/addons/reedcamera/_example/test.tscn @@ -0,0 +1,14 @@ +[gd_scene load_steps=3 format=3 uid="uid://b2rtcqvak066v"] + +[ext_resource type="Texture2D" uid="uid://c673bap4b12fx" path="res://icon.svg" id="1_6ducv"] +[ext_resource type="PackedScene" uid="uid://cw6buluknvjj" path="res://_camera/PlateformerCamera.tscn" id="2_owtx0"] + +[node name="Test" type="Node2D"] + +[node name="Icon" type="Sprite2D" parent="."] +position = Vector2(-1, -2) +texture = ExtResource("1_6ducv") + +[node name="PlateformerCamera" parent="." instance=ExtResource("2_owtx0")] +ignore_rotation = false +position_smoothing_enabled = true diff --git a/addons/reedcamera/_example/test_shake.tres b/addons/reedcamera/_example/test_shake.tres new file mode 100644 index 0000000..0464e5d --- /dev/null +++ b/addons/reedcamera/_example/test_shake.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="ReedCameraShakePreset" load_steps=2 format=3 uid="uid://wm3cmccp1ydl"] + +[ext_resource type="Script" uid="uid://wlqopoksgvjc" path="res://addons/reedcamera/resource/ReedCameraShakePreset.gd" id="1_8o8fw"] + +[resource] +script = ExtResource("1_8o8fw") +amplitude = Vector2(10, 10) +frequency = 100.0 +metadata/_custom_type_script = "uid://wlqopoksgvjc" diff --git a/_asset/icon/camera_anchor_icon.svg b/addons/reedcamera/icon/camera_anchor_icon.svg similarity index 100% rename from _asset/icon/camera_anchor_icon.svg rename to addons/reedcamera/icon/camera_anchor_icon.svg diff --git a/_asset/icon/camera_anchor_icon.svg.import b/addons/reedcamera/icon/camera_anchor_icon.svg.import similarity index 76% rename from _asset/icon/camera_anchor_icon.svg.import rename to addons/reedcamera/icon/camera_anchor_icon.svg.import index 94e3430..983253b 100644 --- a/_asset/icon/camera_anchor_icon.svg.import +++ b/addons/reedcamera/icon/camera_anchor_icon.svg.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://bsdmq0essfmpk" -path="res://.godot/imported/camera_anchor_icon.svg-bc0c9f7b183031f0db701d2e858a9063.ctex" +path="res://.godot/imported/camera_anchor_icon.svg-d54e4ec18108e371c1abc23152af31de.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://_asset/icon/camera_anchor_icon.svg" -dest_files=["res://.godot/imported/camera_anchor_icon.svg-bc0c9f7b183031f0db701d2e858a9063.ctex"] +source_file="res://addons/reedcamera/icon/camera_anchor_icon.svg" +dest_files=["res://.godot/imported/camera_anchor_icon.svg-d54e4ec18108e371c1abc23152af31de.ctex"] [params] diff --git a/addons/reedcamera/ref_counted/ReedCameraShakePlayer.gd b/addons/reedcamera/ref_counted/ReedCameraShakePlayer.gd new file mode 100644 index 0000000..accda02 --- /dev/null +++ b/addons/reedcamera/ref_counted/ReedCameraShakePlayer.gd @@ -0,0 +1,55 @@ +extends RefCounted +class_name ReedCameraShakePlayer + +var _preset: ReedCameraShakePreset +var _time := 0.0 +var _strength := 0.0 +var _active := false + +var _noise := FastNoiseLite.new() +var _noise_seed := randi() + +func play(preset: ReedCameraShakePreset) -> void: + _preset = preset + _time = 0.0 + _strength = 0.0 + _active = true + _noise.seed = _noise_seed + +func stop() -> void: + _active = false + +func is_active() -> bool: + return _active + +func evaluate(delta: float) -> Vector2: + if not _active or not _preset: + return Vector2.ZERO + + _time += delta + + var total := _preset.fade_in + _preset.hold + _preset.fade_out + if _time >= total: + _active = false + return Vector2.ZERO + + # ===== 强度曲线 ===== + if _time < _preset.fade_in: + _strength = _time / _preset.fade_in + elif _time < _preset.fade_in + _preset.hold: + _strength = 1.0 + else: + var t := (_time - _preset.fade_in - _preset.hold) / _preset.fade_out + _strength = 1.0 - t + + # ===== Noise 偏移 ===== + var shake_t := _time * _preset.frequency + var offset := Vector2( + _noise.get_noise_1d(shake_t), + _noise.get_noise_1d(shake_t + 1000) + ) + + return Vector2( + offset.x * _preset.amplitude.x, + offset.y * _preset.amplitude.y + ) * _strength diff --git a/addons/reedcamera/ref_counted/ReedCameraShakePlayer.gd.uid b/addons/reedcamera/ref_counted/ReedCameraShakePlayer.gd.uid new file mode 100644 index 0000000..20c8992 --- /dev/null +++ b/addons/reedcamera/ref_counted/ReedCameraShakePlayer.gd.uid @@ -0,0 +1 @@ +uid://5xuwd0cde3y8 diff --git a/addons/reedcamera/resource/ReedCameraShakePreset.gd b/addons/reedcamera/resource/ReedCameraShakePreset.gd new file mode 100644 index 0000000..6aa10d9 --- /dev/null +++ b/addons/reedcamera/resource/ReedCameraShakePreset.gd @@ -0,0 +1,9 @@ +# CameraShakePreset.gd +extends Resource +class_name ReedCameraShakePreset + +@export var amplitude := Vector2(6, 6) # 最大位移 +@export var frequency := 25.0 # 抖动频率 +@export var fade_in := 0.05 +@export var hold := 0.1 +@export var fade_out := 0.15 diff --git a/addons/reedcamera/resource/ReedCameraShakePreset.gd.uid b/addons/reedcamera/resource/ReedCameraShakePreset.gd.uid new file mode 100644 index 0000000..29637c0 --- /dev/null +++ b/addons/reedcamera/resource/ReedCameraShakePreset.gd.uid @@ -0,0 +1 @@ +uid://wlqopoksgvjc diff --git a/addons/reedcamera/scripts/CameraAnchor.gd b/addons/reedcamera/scripts/CameraAnchor.gd index 96f4dee..75f5acc 100644 --- a/addons/reedcamera/scripts/CameraAnchor.gd +++ b/addons/reedcamera/scripts/CameraAnchor.gd @@ -1,6 +1,6 @@ ##TODO:清楚掉這裏和PhantomCamera相關的部分 @tool -@icon("uid://bsdmq0essfmpk") +@icon("res://addons/reedcamera/icon/camera_anchor_icon.svg") class_name CameraAnchor extends Node2D const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") @@ -10,6 +10,10 @@ const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") @export var priority: int = 0 ##此Anchor是否有效 @export var enabled: bool = true +## ========================= +## Blend Config +## ========================= +@export_subgroup("Blending") ##是否要存在相機過渡時間 @export var use_blend: bool = true ##過度時間 @@ -49,12 +53,6 @@ const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") limit_right = value if Engine.is_editor_hint(): queue_redraw() -@export_subgroup("Follow") -@export var follow_player: bool = false - - - -var _camera_global : Node = null ## 编辑器预览面板设置 @export_group("Editor Preview") @@ -66,6 +64,15 @@ var _camera_global : Node = null @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: @@ -147,7 +154,6 @@ func _get_camera_global() -> Object: if Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME): _camera_global = Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME) - print("") return _camera_global diff --git a/addons/reedcamera/scripts/CameraPointer.gd b/addons/reedcamera/scripts/CameraPointer.gd new file mode 100644 index 0000000..e898c88 --- /dev/null +++ b/addons/reedcamera/scripts/CameraPointer.gd @@ -0,0 +1,125 @@ +@tool +extends Node +class_name CameraPointer + +const _CONSTANTS := preload("res://addons/reedcamera/_data/CameraSystemConst.gd") + +var _camera: Camera2D +var _editor_valid := false +var _runtime_registered := false +var _warned := false + +var _final_offset := Vector2.ZERO +var _base_position: Vector2 + +func _enter_tree() -> void: + if Engine.is_editor_hint(): + call_deferred("_editor_verify") + +func _exit_tree() -> void: + if Engine.is_editor_hint(): + return + + _request_unregister() + +func _process(delta: float) -> void: + if Engine.is_editor_hint(): + return + + if not _camera: + return + + _update_base_position() + _update_camera_offset() + _apply_camera_transform() + +func _update_base_position(): + for child in get_children(): + if child.has_method("get_base_position"): + _base_position = child.get_base_position() + return + + # fallback + _base_position = _camera.global_position + +func _update_camera_offset(): + var offset := Vector2.ZERO + + for child in get_children(): + if child.has_method("get_camera_offset"): + offset += child.get_camera_offset() + + _final_offset = offset + +func _apply_camera_transform(): + _camera.global_position = _base_position + _camera.offset = _final_offset + + +func _notification(what: int) -> void: + if not Engine.is_editor_hint(): + return + + match what: + NOTIFICATION_PARENTED, NOTIFICATION_UNPARENTED: + #print("重設parent") + call_deferred("_editor_verify") + +## 編輯器層面的通過性驗證 +func _editor_verify() -> void: + _camera = _find_camera() + _editor_valid = _camera != null + #print(_editor_valid) + + if not _editor_valid: + _emit_config_warning() + else: + _warned = false + +func _ready() -> void: + if Engine.is_editor_hint(): + return + + _camera = _find_camera() + if not _camera: + return + + _request_register() + +func _find_camera() -> Camera2D: + var p := get_parent() + return p as Camera2D if p is Camera2D else null + +func _request_register() -> void: + var sys := Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME) + if not sys: + return + + _runtime_registered = sys.request_register_pointer(self) + +func _request_unregister() -> void: + if not _runtime_registered: + return + + var sys := Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME) + if sys: + sys.request_unregister_pointer(self) + + _runtime_registered = false + +func _emit_config_warning() -> void: + if not Engine.is_editor_hint(): + return + + if _warned: + return + + push_warning( + "[CameraPointer] Invalid configuration: parent node must be Camera2D. " + + "Current parent: %s" % (get_parent() if get_parent() else "null") + ) + + _warned = true + +func get_camera() -> Camera2D: + return _camera diff --git a/addons/reedcamera/scripts/CameraPointer.gd.uid b/addons/reedcamera/scripts/CameraPointer.gd.uid new file mode 100644 index 0000000..65e3781 --- /dev/null +++ b/addons/reedcamera/scripts/CameraPointer.gd.uid @@ -0,0 +1 @@ +uid://py4h5jxlncro diff --git a/addons/reedcamera/scripts/ReedCameraGlobal.gd b/addons/reedcamera/scripts/ReedCameraGlobal.gd index 5b217d1..7433c67 100644 --- a/addons/reedcamera/scripts/ReedCameraGlobal.gd +++ b/addons/reedcamera/scripts/ReedCameraGlobal.gd @@ -5,6 +5,63 @@ const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") var _screen_size : Vector2i +var _camera_points : Array[CameraPointer] +var _current_camera_point : CameraPointer + + +signal anchor_registered(anchor: CameraAnchor) +signal anchor_unregistered(anchor: CameraAnchor) + +var _anchors : Array[CameraAnchor] + +#region 相機指針 +func request_register_pointer(ptr: CameraPointer) -> bool: + if not is_instance_valid(ptr): + return false + + var cam := ptr.get_camera() + if not is_instance_valid(cam): + return false # System 决定:无效 camera 不接受注册 + + # 去重 + if ptr in _camera_points: + _current_camera_point = ptr + return true + + _camera_points.append(ptr) + + # 你可以在这里做选择策略:比如最新优先/priority + _current_camera_point = ptr + return true + +func request_unregister_pointer(ptr: CameraPointer) -> void: + _camera_points.erase(ptr) + if _current_camera_point == ptr: + _current_camera_point = _camera_points.back() if _camera_points.size() > 0 else null + +func get_current_camera_pointer() -> CameraPointer: + return _current_camera_point + +func get_camera() -> Camera2D: + return _current_camera_point.get_camera() if _current_camera_point else null +#endregion + +#region 相機錨點 +func register_anchor(anchor: CameraAnchor) -> void: + if anchor in _anchors: + return + _anchors.append(anchor) + anchor_registered.emit(anchor) + +func unregister_anchor(anchor: CameraAnchor) -> void: + if _anchors.has(anchor): + _anchors.erase(anchor) + anchor_unregistered.emit(anchor) + +func get_all_anchors() -> Array[CameraAnchor]: + return _anchors.duplicate() +#endregion + func _enter_tree() -> void: if not Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME): Engine.register_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME, self) diff --git a/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd b/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd new file mode 100644 index 0000000..69c1050 --- /dev/null +++ b/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd @@ -0,0 +1,99 @@ +extends Node +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 _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 _current_position: Vector2 +var _target_position: Vector2 +#var _tween: Tween +var _has_target := false + +## ========================= +## Lifecycle +## ========================= + +func _ready() -> void: + var sys := Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME) + if not sys: + return + + _anchors = sys.get_all_anchors() + + sys.anchor_registered.connect(_on_anchor_added) + sys.anchor_unregistered.connect(_on_anchor_removed) + + _evaluate() + +func _on_anchor_added(anchor: CameraAnchor) -> void: + _anchors.append(anchor) + _evaluate() + +func _on_anchor_removed(anchor: CameraAnchor) -> void: + _anchors.erase(anchor) + if _current_anchor == anchor: + _current_anchor = null + _evaluate() + +func _evaluate() -> 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 + var target := anchor.global_position + + if _tween and _tween.is_running(): + _tween.kill() + + if not anchor.use_blend or anchor.blend_time <= 0.0: + _current_position = target + return + + _tween = get_tree().create_tween() + _tween.set_trans(Tween.TRANS_CUBIC) + _tween.set_ease(Tween.EASE_OUT) + _tween.tween_property(self, "_current_position", target, anchor.blend_time) + +## ========================= +## CameraPointer Interface +## ========================= +func get_base_position() -> Vector2: + return _current_position diff --git a/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd.uid b/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd.uid new file mode 100644 index 0000000..a7acd0c --- /dev/null +++ b/addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd.uid @@ -0,0 +1 @@ +uid://bhl5it46hv4n2 diff --git a/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd b/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd new file mode 100644 index 0000000..03fe761 --- /dev/null +++ b/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd @@ -0,0 +1,66 @@ +@tool +extends Node +class_name ReedCameraShakeController + +## ========================= +## Config +## ========================= +@export var enabled := true + +## ========================= +## Runtime +## ========================= + +var _players: Array[ReedCameraShakePlayer] = [] +var _current_offset := Vector2.ZERO + +func _ready() -> void: + if Engine.is_editor_hint(): + return + set_process(true) + +func _exit_tree() -> void: + _players.clear() + _current_offset = Vector2.ZERO + +func _process(delta: float) -> void: + if Engine.is_editor_hint(): + return + + if not enabled: + _current_offset = Vector2.ZERO + return + + var offset := Vector2.ZERO + + for p in _players: + offset += p.evaluate(delta) + + # 移除已经结束的 player + _players = _players.filter(func(p): return p.is_active()) + + _current_offset = offset + +## ========================= +## Public API +## ========================= +func play_shake(preset: ReedCameraShakePreset) -> void: + if not preset: + return + + var player := ReedCameraShakePlayer.new() + player.play(preset) + _players.append(player) + +func stop_all() -> void: + for p in _players: + p.stop() + _players.clear() + _current_offset = Vector2.ZERO + +## ========================= +## Pipeline Output +## ========================= + +func get_camera_offset() -> Vector2: + return _current_offset diff --git a/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd.uid b/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd.uid new file mode 100644 index 0000000..6cf88f3 --- /dev/null +++ b/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd.uid @@ -0,0 +1 @@ +uid://dwr1s51svvank diff --git a/project.godot b/project.godot index 79fd9e2..00b590f 100644 --- a/project.godot +++ b/project.godot @@ -17,16 +17,13 @@ config/icon="res://icon.svg" [autoload] -ReedCameraSystem="*res://addons/reedcamera/scripts/ReedCameraGlobal.gd" -CameraSystem="*res://_camera/CameraSystem.tscn" GlobalEvent="*res://_shared/GlobalEvent.gd" ReedVFX="*res://addons/reedfx/vfx/ReedVFXSystem.tscn" ReedSceneRegistry="*res://addons/reedscene/scene/SceneRegistry.gd" +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" From aee69ab5e8d62760ba599b2abf16b07da8f37b75 Mon Sep 17 00:00:00 2001 From: RedisTKey Date: Tue, 13 Jan 2026 00:52:36 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E7=9B=B8=E6=9C=BA=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=9B=B4=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" From 26b30eb032dfac964306aca55b84e1bede74c9ee Mon Sep 17 00:00:00 2001 From: RedisTKey Date: Tue, 13 Jan 2026 10:32:36 +0800 Subject: [PATCH 4/5] camera follower tool designing.... --- _camera/PlateformerCamera.tscn | 2 - addons/reedcamera/scripts/CameraPointer.gd | 19 +++++ .../camera_tools/CameraFollowController.gd | 84 +++++++++++++++++++ .../CameraFollowController.gd.uid | 1 + 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 addons/reedcamera/scripts/camera_tools/CameraFollowController.gd create mode 100644 addons/reedcamera/scripts/camera_tools/CameraFollowController.gd.uid diff --git a/_camera/PlateformerCamera.tscn b/_camera/PlateformerCamera.tscn index 935cd8e..e5ecf16 100644 --- a/_camera/PlateformerCamera.tscn +++ b/_camera/PlateformerCamera.tscn @@ -14,9 +14,7 @@ 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"] diff --git a/addons/reedcamera/scripts/CameraPointer.gd b/addons/reedcamera/scripts/CameraPointer.gd index 68d7ef1..461ce50 100644 --- a/addons/reedcamera/scripts/CameraPointer.gd +++ b/addons/reedcamera/scripts/CameraPointer.gd @@ -164,5 +164,24 @@ func _emit_config_warning() -> void: _warned = true +## ========================= +## Public API +## ========================= +##获取相机 func get_camera() -> Camera2D: return _camera + +##获取特定工具 +func get_tool(tool_class: Script) -> Node: + for c in get_children(): + if c.get_script() == tool_class: + return c + return null + +##获取所有工具 +func get_tools() -> Array[CameraToolBasic]: + var out: Array[CameraToolBasic] = [] + for c in get_children(): + if c is CameraToolBasic: + out.append(c) + return out diff --git a/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd b/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd new file mode 100644 index 0000000..92eb39d --- /dev/null +++ b/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd @@ -0,0 +1,84 @@ +extends CameraToolBasic +class_name ReedCameraFollowController + +const _CONSTANTS := preload("res://addons/reedcamera/_data/CameraSystemConst.gd") + +@export_group("Dead Zone") +@export var dead_zone_ratio := Rect2( + Vector2.ZERO, # 屏幕中心 + Vector2(0.4, 0.4) # 宽高(屏幕比例) +) + +@export_group("Follow") +@export var enabled_follow: bool = true +@export var follow_lerp := 0.12 # 0~1,越大越“跟手”,越小越“蔚蓝感”的滞后 + +var _follow_node2d : Node2D = null +var _desired_pos := Vector2.ZERO +var _camera: Camera2D = null + +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 + + # 初始化 + if not _initialized: + _pos = cam.global_position + _desired_pos = _pos + _initialized = true + + var view_rect := _get_camera_view_rect(cam) + var dead_rect := _get_dead_zone_world_rect(view_rect) + + var p := player.global_position + var delta_move := Vector2.ZERO + + if p.x < dead_rect.position.x: + delta_move.x = p.x - dead_rect.position.x + elif p.x > dead_rect.position.x + dead_rect.size.x: + delta_move.x = p.x - (dead_rect.position.x + dead_rect.size.x) + + if p.y < dead_rect.position.y: + delta_move.y = p.y - dead_rect.position.y + elif p.y > dead_rect.position.y + dead_rect.size.y: + delta_move.y = p.y - (dead_rect.position.y + dead_rect.size.y) + + if delta_move != Vector2.ZERO: + _desired_pos += delta_move + + # 可选 room clamp + if use_room_clamp: + _desired_pos = _desired_pos.clamp( + room_world_rect.position, + room_world_rect.position + room_world_rect.size + ) + + # 蔚蓝感:滞后 lerp + _pos = _pos.lerp(_desired_pos, follow_lerp) + +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_view_rect(cam: Camera2D) -> Rect2: + var screen_size := cam.get_viewport_rect().size + var zoom := cam.zoom + zoom.x = maxf(zoom.x, 0.001) + zoom.y = maxf(zoom.y, 0.001) + + var size := screen_size / zoom + return Rect2(cam.global_position - size * 0.5, size) diff --git a/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd.uid b/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd.uid new file mode 100644 index 0000000..f27968d --- /dev/null +++ b/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd.uid @@ -0,0 +1 @@ +uid://wl83yn33gu3m From b8373aed65bb63ed5cf5f53502a9d19ce91f3e5b Mon Sep 17 00:00:00 2001 From: Reed Date: Tue, 13 Jan 2026 16:52:45 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E7=9B=B8=E6=9C=BA=E5=8A=9F=E8=83=BDbeta?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _camera/PlateformerCamera.tscn | 8 +- _player/avatar.gd | 1 - _scene/level1/l0_s0.tscn | 1 - _scene/level1/l1_s2.tscn | 5 +- .../default_switch.tres | 21 +- _shared/GlobalEvent.gd | 28 +++ addons/reedcamera/_data/CameraSystemConst.gd | 4 + addons/reedcamera/scripts/CameraPointer.gd | 26 ++- addons/reedcamera/scripts/ScreenRatioRect.gd | 114 ++++++++++ .../reedcamera/scripts/ScreenRatioRect.gd.uid | 1 + .../camera_tools/CameraFollowController.gd | 201 ++++++++++++++---- .../camera_tools/CameraShakeController.gd | 1 + .../scripts/camera_tools/DeadZoneDebug.tscn | 9 + .../scene/scene_trigger/STT_Autoload.gd | 6 + .../scene/scene_trigger/STT_Autoload.gd.uid | 1 + 15 files changed, 369 insertions(+), 58 deletions(-) create mode 100644 addons/reedcamera/scripts/ScreenRatioRect.gd create mode 100644 addons/reedcamera/scripts/ScreenRatioRect.gd.uid create mode 100644 addons/reedcamera/scripts/camera_tools/DeadZoneDebug.tscn create mode 100644 addons/reedscene/scene/scene_trigger/STT_Autoload.gd create mode 100644 addons/reedscene/scene/scene_trigger/STT_Autoload.gd.uid diff --git a/_camera/PlateformerCamera.tscn b/_camera/PlateformerCamera.tscn index e5ecf16..0666550 100644 --- a/_camera/PlateformerCamera.tscn +++ b/_camera/PlateformerCamera.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=3 uid="uid://cw6buluknvjj"] +[gd_scene load_steps=6 format=3 uid="uid://cw6buluknvjj"] [ext_resource type="Script" uid="uid://djk7tg2puphgv" path="res://_camera/camera_test.gd" id="1_05blt"] [ext_resource type="Script" uid="uid://py4h5jxlncro" path="res://addons/reedcamera/scripts/CameraPointer.gd" id="1_e7rkk"] [ext_resource type="Script" uid="uid://dwr1s51svvank" path="res://addons/reedcamera/scripts/camera_tools/CameraShakeController.gd" id="2_rbequ"] [ext_resource type="Script" uid="uid://bhl5it46hv4n2" path="res://addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd" id="4_877nu"] +[ext_resource type="Script" uid="uid://wl83yn33gu3m" path="res://addons/reedcamera/scripts/camera_tools/CameraFollowController.gd" id="5_kdnmf"] [node name="PlateformerCamera" type="Camera2D"] script = ExtResource("1_05blt") @@ -22,3 +23,8 @@ script = ExtResource("4_877nu") affect_position = true affect_zoom = true metadata/_custom_type_script = "uid://bhl5it46hv4n2" + +[node name="ReedCameraFollowController" type="Node" parent="CameraPointer"] +script = ExtResource("5_kdnmf") +affect_position = true +metadata/_custom_type_script = "uid://wl83yn33gu3m" diff --git a/_player/avatar.gd b/_player/avatar.gd index aa000c7..6408d0c 100644 --- a/_player/avatar.gd +++ b/_player/avatar.gd @@ -76,7 +76,6 @@ func _draw() -> void: func _process(delta: float) -> void: queue_redraw() - func set_move_input(dir: Vector2) -> void: m_input_intent_direction = dir diff --git a/_scene/level1/l0_s0.tscn b/_scene/level1/l0_s0.tscn index aad64c3..12c1031 100644 --- a/_scene/level1/l0_s0.tscn +++ b/_scene/level1/l0_s0.tscn @@ -119,7 +119,6 @@ script = ExtResource("10_g4f48") [node name="CameraAnchor" parent="Props" instance=ExtResource("11_o5yb1")] position = Vector2(-39, 1203) -zoom = Vector2(0.75, 0.75) [node name="[Prop_0000]" type="Node" parent="Props/CameraAnchor"] script = ExtResource("12_vhd7q") diff --git a/_scene/level1/l1_s2.tscn b/_scene/level1/l1_s2.tscn index 47b0a66..0d89e3d 100644 --- a/_scene/level1/l1_s2.tscn +++ b/_scene/level1/l1_s2.tscn @@ -134,8 +134,7 @@ init_act_id = 0 script = ExtResource("7_8ou3l") [node name="CameraAnchor" parent="Props" instance=ExtResource("8_dq7pn")] -position = Vector2(540, -400) -zoom = Vector2(0.75, 0.75) +position = Vector2(336, -385) limit_top = -335 limit_bottom = 240 limit_left = -427 @@ -159,10 +158,10 @@ state_id = 1 effects = Array[ExtResource("12_fmhh5")]([ExtResource("14_nnp13")]) [node name="PlayerTriggerVolumn" parent="Props" instance=ExtResource("15_lg3ok")] -visible = false position = Vector2(560, -480) [node name="CollisionShape2D" type="CollisionShape2D" parent="Props/PlayerTriggerVolumn"] +visible = false show_behind_parent = true position = Vector2(-25.5, -2) shape = SubResource("RectangleShape2D_oupin") diff --git a/_scene/scene_trigger_resource/default_switch.tres b/_scene/scene_trigger_resource/default_switch.tres index 1e18f5d..b1957b6 100644 --- a/_scene/scene_trigger_resource/default_switch.tres +++ b/_scene/scene_trigger_resource/default_switch.tres @@ -1,4 +1,4 @@ -[gd_resource type="Resource" script_class="SceneTrigger" load_steps=16 format=3 uid="uid://bym4pb0ellj7b"] +[gd_resource type="Resource" script_class="SceneTrigger" load_steps=20 format=3 uid="uid://bym4pb0ellj7b"] [ext_resource type="Script" uid="uid://baamspwt4rm4r" path="res://addons/reedscene/scene/guard.gd" id="1_ebfhi"] [ext_resource type="Script" uid="uid://ons77en82uls" path="res://addons/reedscene/scene/scene_trigger/base/SceneTrigger.gd" id="2_cq8o1"] @@ -7,6 +7,7 @@ [ext_resource type="Script" uid="uid://bjstkg23cq6vq" path="res://addons/reedscene/scene/scene_trigger/STE_SwitchAct.gd" id="5_m44nk"] [ext_resource type="Script" uid="uid://c8qq8400vebpg" path="res://addons/reedscene/scene/scene_trigger/STT_Self.gd" id="6_qb8kc"] [ext_resource type="Script" uid="uid://dcn3k2vc6on0c" path="res://addons/reedscene/scene/scene_trigger/STT_Tree.gd" id="7_547il"] +[ext_resource type="Script" uid="uid://cwxwsfl1mx7kc" path="res://addons/reedscene/scene/scene_trigger/STT_Autoload.gd" id="8_cq8o1"] [ext_resource type="Script" uid="uid://cdprpen0jyr6d" path="res://addons/reedscene/scene/scene_trigger/STR_NodePath.gd" id="8_kur88"] [sub_resource type="Resource" id="Resource_yc616"] @@ -41,6 +42,22 @@ target = SubResource("Resource_kdh4c") effect = Array[ExtResource("4_g7ixm")]([SubResource("Resource_jd40h")]) metadata/_custom_type_script = "uid://dxj5vimigc651" +[sub_resource type="Resource" id="Resource_cq8o1"] +script = ExtResource("5_m44nk") +func_name = &"player_follow_camera" +metadata/_custom_type_script = "uid://bjstkg23cq6vq" + +[sub_resource type="Resource" id="Resource_m0qh3"] +script = ExtResource("8_cq8o1") +autoload_name = &"GlobalEvent" +metadata/_custom_type_script = "uid://cwxwsfl1mx7kc" + +[sub_resource type="Resource" id="Resource_g7ixm"] +script = ExtResource("3_m0qh3") +target = SubResource("Resource_m0qh3") +effect = Array[ExtResource("4_g7ixm")]([SubResource("Resource_cq8o1")]) +metadata/_custom_type_script = "uid://dxj5vimigc651" + [sub_resource type="Resource" id="Resource_8u4ru"] script = ExtResource("8_kur88") node_path = NodePath("../Props/PlayerTriggerVolumn") @@ -50,5 +67,5 @@ metadata/_custom_type_script = "uid://cdprpen0jyr6d" [resource] script = ExtResource("2_cq8o1") trigger_register_conifg = SubResource("Resource_8u4ru") -trigger_effect_pairs = Array[ExtResource("3_m0qh3")]([SubResource("Resource_vv5v7"), SubResource("Resource_ig5jt")]) +trigger_effect_pairs = Array[ExtResource("3_m0qh3")]([SubResource("Resource_vv5v7"), SubResource("Resource_ig5jt"), SubResource("Resource_g7ixm")]) metadata/_custom_type_script = "uid://ons77en82uls" diff --git a/_shared/GlobalEvent.gd b/_shared/GlobalEvent.gd index dfa9a06..698bc24 100644 --- a/_shared/GlobalEvent.gd +++ b/_shared/GlobalEvent.gd @@ -6,6 +6,8 @@ signal player_dead(player: Player) var _cached_player_controller: PlayerController var _cached_player: Player +var _camera_follower : Node = null + ## player controller進入tree會注冊自己到Global func register_player_controller(pc: PlayerController) -> PlayerController: if not pc: return null @@ -18,6 +20,10 @@ func register_player(player: Player) -> Player: if not player: return null _cached_player = player + + ##如果我们缓存了一个camera_follower,在玩家重生的时候我们会让follower自动绑定 + if _camera_follower: + _camera_follower.register_follower(_cached_player) return _cached_player ## 外部快速获取Player @@ -28,6 +34,28 @@ func get_player() -> Player: func get_player_controller() -> PlayerController: return _cached_player_controller +func player_follow_camera() -> void: + if not _cached_player:return + + var cam := ReedCameraSystem.get_current_camera_pointer() as CameraPointer + if not cam:return + + var follower := cam.get_tool_by_type(CameraPointer.ToolType.FOLLOWER) + if not follower:return + + follower.register_follower(_cached_player) + _camera_follower = follower + +func player_unfollow_camera() -> void: + var cam := ReedCameraSystem.get_current_camera_pointer() as CameraPointer + if not cam:return + + var follower := cam.get_tool_by_type(CameraPointer.ToolType.FOLLOWER) + if not follower:return + + follower.unregister_follower() + _camera_follower = null + ## 外部用于监听Player死亡 func boradcast_player_dead_event(player:Player) -> void: player_dead.emit(player) diff --git a/addons/reedcamera/_data/CameraSystemConst.gd b/addons/reedcamera/_data/CameraSystemConst.gd index 88a49d5..46ab778 100644 --- a/addons/reedcamera/_data/CameraSystemConst.gd +++ b/addons/reedcamera/_data/CameraSystemConst.gd @@ -7,6 +7,10 @@ const CAMERA_SYSTEM_NAME : StringName = "ReedCameraSystem" const CAMERA_ANCHOR_NAME : StringName = "CameraAnchor" const CAMERA_FOLLOWER_NAME : StringName = "CameraFollower" +const CAMERA_TOOL_SHAKER : String = "res://addons/reedcamera/scripts/camera_tools/CameraShakeController.gd" +const CAMERA_TOOL_ANCHOR : String = "res://addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd" +const CAMERA_TOOL_FOLLOWER : String = "res://addons/reedcamera/scripts/camera_tools/CameraFollowController.gd" + #endregion #region GroupName diff --git a/addons/reedcamera/scripts/CameraPointer.gd b/addons/reedcamera/scripts/CameraPointer.gd index 461ce50..5774d10 100644 --- a/addons/reedcamera/scripts/CameraPointer.gd +++ b/addons/reedcamera/scripts/CameraPointer.gd @@ -4,6 +4,20 @@ class_name CameraPointer const _CONSTANTS := preload("res://addons/reedcamera/_data/CameraSystemConst.gd") +## 相机指针所能持有的工具种类 +enum ToolType{ + SHAKER, + ANCHOR, + FOLLOWER +} + +## 相机工具和脚本的映射 +const type_script_map:Dictionary = { + ToolType.SHAKER : preload("res://addons/reedcamera/scripts/camera_tools/CameraShakeController.gd"), + ToolType.ANCHOR : preload("res://addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd"), + ToolType.FOLLOWER : preload("res://addons/reedcamera/scripts/camera_tools/CameraFollowController.gd") +} + var _camera: Camera2D var _editor_valid := false var _runtime_registered := false @@ -17,6 +31,7 @@ var _pos_dirty := false var _offset_dirty := false var _zoom_dirty := false + func _enter_tree() -> void: if Engine.is_editor_hint(): call_deferred("_editor_verify") @@ -122,10 +137,9 @@ func _editor_verify() -> void: func _ready() -> void: if Engine.is_editor_hint(): return - - _camera = _find_camera() + if not _camera: - return + _camera = _find_camera() _request_register() @@ -169,6 +183,8 @@ func _emit_config_warning() -> void: ## ========================= ##获取相机 func get_camera() -> Camera2D: + if not _camera: + _camera = _find_camera() return _camera ##获取特定工具 @@ -178,6 +194,10 @@ func get_tool(tool_class: Script) -> Node: return c return null +##通过类型返回工具 +func get_tool_by_type(type:ToolType) -> Node: + return get_tool(type_script_map.get(type)) + ##获取所有工具 func get_tools() -> Array[CameraToolBasic]: var out: Array[CameraToolBasic] = [] diff --git a/addons/reedcamera/scripts/ScreenRatioRect.gd b/addons/reedcamera/scripts/ScreenRatioRect.gd new file mode 100644 index 0000000..b7fdd51 --- /dev/null +++ b/addons/reedcamera/scripts/ScreenRatioRect.gd @@ -0,0 +1,114 @@ +@tool +extends Node2D +class_name ScreenRatioRect + +# =============================== +# Internal State +# =============================== +var _rect: Rect2 = Rect2() +var _line_color: Color = Color(0.2, 0.9, 0.4, 0.9) +var _line_width: float = 2.0 + +var _target_node: Node2D = null +var _target_screen_pos: Vector2 = Vector2.ZERO +var _target_radius: float = 6.0 +var _target_color: Color = Color(1, 0.3, 0.3, 0.9) + +func _process(_delta: float) -> void: + if Engine.is_editor_hint(): + return + + if _target_node: + _update_target_screen_pos() + queue_redraw() + +# =============================== +# Public API +# =============================== +## 通过屏幕比例设置(0~1) +func set_ratio(ratio: Vector2) -> void: + var viewport_size := get_viewport_rect().size + var size := viewport_size * ratio + var pos := (viewport_size - size) * 0.5 + set_rect(Rect2(pos, size)) + +## 直接设置屏幕空间 Rect +func set_rect(rect: Rect2) -> void: + _rect = rect + queue_redraw() + +## 设置绘制风格 +func set_style(color: Color, width: float = 2.0) -> void: + _line_color = color + _line_width = width + queue_redraw() + +## 设置追踪点 +func set_target_node(node: Node2D) -> void: + _target_node = node + _update_target_screen_pos() + queue_redraw() + +## 更新targetnode在屏幕的位置 +func _update_target_screen_pos() -> void: + if not _target_node: + return + + _target_screen_pos = \ + _target_node.get_global_transform_with_canvas().get_origin() + +## 可选:快速隐藏 / 显示 +func clear() -> void: + _target_node = null + _rect = Rect2() + queue_redraw() + +# =============================== +# Draw +# =============================== +func _draw() -> void: + if _rect.size == Vector2.ZERO: + return + + var left := _rect.position.x + var right := _rect.position.x + _rect.size.x + var top := _rect.position.y + var bottom := _rect.position.y + _rect.size.y + + var vp_size := get_viewport_rect().size + + # 上边线 + draw_line( + Vector2(0, top), + Vector2(vp_size.x, top), + _line_color, + _line_width + ) + + # 下边线 + draw_line( + Vector2(0, bottom), + Vector2(vp_size.x, bottom), + _line_color, + _line_width + ) + + # 左边线 + draw_line( + Vector2(left, 0), + Vector2(left, vp_size.y), + _line_color, + _line_width + ) + + # 右边线 + draw_line( + Vector2(right, 0), + Vector2(right, vp_size.y), + _line_color, + _line_width + ) + + ##绘制角色位置锚点 + if _target_node: + draw_circle(_target_screen_pos, _target_radius, _target_color) diff --git a/addons/reedcamera/scripts/ScreenRatioRect.gd.uid b/addons/reedcamera/scripts/ScreenRatioRect.gd.uid new file mode 100644 index 0000000..6d51f88 --- /dev/null +++ b/addons/reedcamera/scripts/ScreenRatioRect.gd.uid @@ -0,0 +1 @@ +uid://dckyyfvj25bxf diff --git a/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd b/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd index 92eb39d..1e5e31f 100644 --- a/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd +++ b/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd @@ -2,67 +2,147 @@ 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 := Rect2( - Vector2.ZERO, # 屏幕中心 - Vector2(0.4, 0.4) # 宽高(屏幕比例) -) +@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,越大越“跟手”,越小越“蔚蓝感”的滞后 +##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 _desired_pos := Vector2.ZERO +##最终的位置 +var _final_position: Vector2 + +##缓存全局系统 +var _sys: Object = null +##缓存DebugTool +var _screen_ratio_rect_draw_tool: Node = null +##缓存camera var _camera: Camera2D = null -func _update_follow(delta: float) -> void: +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 - - # 初始化 - if not _initialized: - _pos = cam.global_position - _desired_pos = _pos - _initialized = true - - var view_rect := _get_camera_view_rect(cam) - var dead_rect := _get_dead_zone_world_rect(view_rect) - - var p := player.global_position - var delta_move := Vector2.ZERO - - if p.x < dead_rect.position.x: - delta_move.x = p.x - dead_rect.position.x - elif p.x > dead_rect.position.x + dead_rect.size.x: - delta_move.x = p.x - (dead_rect.position.x + dead_rect.size.x) - - if p.y < dead_rect.position.y: - delta_move.y = p.y - dead_rect.position.y - elif p.y > dead_rect.position.y + dead_rect.size.y: - delta_move.y = p.y - (dead_rect.position.y + dead_rect.size.y) - - if delta_move != Vector2.ZERO: - _desired_pos += delta_move - - # 可选 room clamp - if use_room_clamp: - _desired_pos = _desired_pos.clamp( - room_world_rect.position, - room_world_rect.position + room_world_rect.size + + # 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 + ##如果存在move_dir,则我们认为相机需要chasing player + + var desired_pos := _final_position + world_velocity * delta - # 蔚蓝感:滞后 lerp - _pos = _pos.lerp(_desired_pos, follow_lerp) + 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 @@ -74,11 +154,38 @@ func _get_camera_from_pointer() -> Camera2D: return _camera -func _get_camera_view_rect(cam: Camera2D) -> Rect2: - var screen_size := cam.get_viewport_rect().size - var zoom := cam.zoom - zoom.x = maxf(zoom.x, 0.001) - zoom.y = maxf(zoom.y, 0.001) +## 获取全局相机管理器 +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 - var size := screen_size / zoom - return Rect2(cam.global_position - size * 0.5, size) + +##外部调用,设置一个相机系统的跟随者 +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 diff --git a/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd b/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd index 1ee07eb..2b48900 100644 --- a/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd +++ b/addons/reedcamera/scripts/camera_tools/CameraShakeController.gd @@ -11,6 +11,7 @@ var _current_offset := Vector2.ZERO func _ready() -> void: if Engine.is_editor_hint(): return + set_process(true) func _exit_tree() -> void: diff --git a/addons/reedcamera/scripts/camera_tools/DeadZoneDebug.tscn b/addons/reedcamera/scripts/camera_tools/DeadZoneDebug.tscn new file mode 100644 index 0000000..5f9004a --- /dev/null +++ b/addons/reedcamera/scripts/camera_tools/DeadZoneDebug.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=3 uid="uid://csx2bji7s6xnl"] + +[ext_resource type="Script" uid="uid://dckyyfvj25bxf" path="res://addons/reedcamera/scripts/ScreenRatioRect.gd" id="1_ljod6"] + +[node name="DeadZoneDebug" type="CanvasLayer"] +layer = 1000 + +[node name="ScreenRatioRect" type="Node2D" parent="."] +script = ExtResource("1_ljod6") diff --git a/addons/reedscene/scene/scene_trigger/STT_Autoload.gd b/addons/reedscene/scene/scene_trigger/STT_Autoload.gd new file mode 100644 index 0000000..f4fae1f --- /dev/null +++ b/addons/reedscene/scene/scene_trigger/STT_Autoload.gd @@ -0,0 +1,6 @@ +class_name STT_Autoload extends SceneTriggerTarget + +@export var autoload_name: StringName + +func get_effect_target(owner: Node) -> Object: + return owner.get_tree().root.get_node_or_null("/root/%s" % autoload_name) diff --git a/addons/reedscene/scene/scene_trigger/STT_Autoload.gd.uid b/addons/reedscene/scene/scene_trigger/STT_Autoload.gd.uid new file mode 100644 index 0000000..1a3c930 --- /dev/null +++ b/addons/reedscene/scene/scene_trigger/STT_Autoload.gd.uid @@ -0,0 +1 @@ +uid://cwxwsfl1mx7kc