Compare commits

..

4 Commits

63 changed files with 1016 additions and 284 deletions

BIN
_asset/ksw/character.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dted7geb331y2"
path="res://.godot/imported/character.png-7c44b4524e7faa111c3b7a3476538724.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://_asset/ksw/character.png"
dest_files=["res://.godot/imported/character.png-7c44b4524e7faa111c3b7a3476538724.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -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")

265
_camera/CameraSystem.gd Normal file
View File

@ -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

View File

@ -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")

View File

@ -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"

View File

@ -0,0 +1,9 @@
extends RemoteTransform2D
func _ready() -> void:
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()

View File

@ -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

View File

@ -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")

View File

@ -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")

View File

@ -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")

8
_camera/camera_test.gd Normal file
View File

@ -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)

View File

@ -0,0 +1 @@
uid://djk7tg2puphgv

View File

@ -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"]
@ -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")

View File

@ -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"]

View File

@ -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"]

View File

@ -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()

View File

@ -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

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -1,5 +1,6 @@
extends Node
signal player_spawned(player: Player)
signal player_dead(player: Player)
var _cached_player_controller: PlayerController

View File

@ -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

View File

@ -1,8 +0,0 @@
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()

View File

@ -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

View File

@ -0,0 +1 @@
uid://bpsj1nyihan67

View File

@ -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

View File

@ -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"

View File

Before

Width:  |  Height:  |  Size: 869 B

After

Width:  |  Height:  |  Size: 869 B

View File

@ -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]

View File

@ -0,0 +1,7 @@
[plugin]
name="ReedCamera"
description="Reed zhu 製作的用於平臺跳躍類游戲的簡易Camera2D插件"
author="ReedZhu"
version=""
script="reedcamera.gd"

View File

@ -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

View File

@ -0,0 +1 @@
uid://dqmq1t1dmsy2

View File

@ -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

View File

@ -0,0 +1 @@
uid://5xuwd0cde3y8

View File

@ -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

View File

@ -0,0 +1 @@
uid://wlqopoksgvjc

View File

@ -1,12 +1,19 @@
##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")
@export_group("Anchor Config")
##该priority不会直接修改Anchor的priority只用作初始化
@export var priority: int = 0
##此Anchor是否有效
@export var enabled: bool = true
## =========================
## Blend Config
## =========================
@export_subgroup("Blending")
##是否要存在相機過渡時間
@export var use_blend: bool = true
##過度時間
@ -46,12 +53,6 @@ class_name CameraAnchor extends Node2D
limit_right = value
if Engine.is_editor_hint():
queue_redraw()
@export_subgroup("Follow")
@export var follow_player: bool = false
var _pcam_manager: Node = null
## 编辑器预览面板设置
@export_group("Editor Preview")
@ -63,6 +64,15 @@ var _pcam_manager: 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:
@ -76,25 +86,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,9 +124,10 @@ 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)
@ -133,11 +141,24 @@ func _camera_frame_rect_like_phantom() -> Rect2:
# 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)
return _camera_global
func push_camera() -> void:
CameraSystem.reset_all_camera_priority()
#CameraSystem.reset_all_camera_priority()
_priority = 1000
func pop_camera() -> void:

View File

@ -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

View File

@ -0,0 +1 @@
uid://py4h5jxlncro

View File

@ -0,0 +1,91 @@
@tool
extends Node
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)
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

View File

@ -0,0 +1 @@
uid://dqtfis2553nhn

View File

@ -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

View File

@ -0,0 +1 @@
uid://bhl5it46hv4n2

View File

@ -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

View File

@ -0,0 +1 @@
uid://dwr1s51svvank

View File

@ -10,29 +10,27 @@ 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"
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"
[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 +40,8 @@ folder_colors={
[global_group]
ROOM="房间分组其下存在所有的Room"
PLAYER="玩家分组,其下只存在玩家控制器"
GRAPABLE=""
GRAPABLE="進入該分組的Collision視爲可以被玩家抓握"
PLAYER_RESPAWN="所有的PlayerRespawnPoint的绑定Group"
[input]