相机功能更新

This commit is contained in:
RedisTKey 2026-01-13 00:52:36 +08:00
parent 98178a2bd2
commit aee69ab5e8
13 changed files with 204 additions and 68 deletions

View File

@ -4,3 +4,4 @@
[node name="CameraAnchor" type="Node2D"] [node name="CameraAnchor" type="Node2D"]
script = ExtResource("1_1b11o") script = ExtResource("1_1b11o")
zoom = Vector2(0.75, 0.75)

View File

@ -14,8 +14,13 @@ metadata/_custom_type_script = "uid://py4h5jxlncro"
[node name="ReedCameraShakeController" type="Node" parent="CameraPointer"] [node name="ReedCameraShakeController" type="Node" parent="CameraPointer"]
script = ExtResource("2_rbequ") script = ExtResource("2_rbequ")
affect_position = null
affect_offset = true
affect_zoom = null
metadata/_custom_type_script = "uid://dwr1s51svvank" metadata/_custom_type_script = "uid://dwr1s51svvank"
[node name="ReedCameraAnchorController" type="Node" parent="CameraPointer"] [node name="ReedCameraAnchorController" type="Node" parent="CameraPointer"]
script = ExtResource("4_877nu") script = ExtResource("4_877nu")
affect_position = true
affect_zoom = true
metadata/_custom_type_script = "uid://bhl5it46hv4n2" metadata/_custom_type_script = "uid://bhl5it46hv4n2"

View File

@ -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="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://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://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://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://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"] [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="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="L0_S0" parent="." instance=ExtResource("3_4ifj7")]
[node name="L1_S1" parent="." instance=ExtResource("4_m1t3p")] [node name="L1_S1" parent="." instance=ExtResource("4_m1t3p")]

View File

@ -119,7 +119,6 @@ script = ExtResource("7_hd3du")
[node name="CameraAnchor" parent="Props" instance=ExtResource("8_vjpkl")] [node name="CameraAnchor" parent="Props" instance=ExtResource("8_vjpkl")]
position = Vector2(-41, 0) position = Vector2(-41, 0)
zoom = Vector2(0.75, 0.75)
[node name="[Prop_0000]" type="Node" parent="Props/CameraAnchor"] [node name="[Prop_0000]" type="Node" parent="Props/CameraAnchor"]
script = ExtResource("9_ctwrc") script = ExtResource("9_ctwrc")

View File

@ -140,7 +140,6 @@ limit_top = -335
limit_bottom = 240 limit_bottom = 240
limit_left = -427 limit_left = -427
limit_right = 427 limit_right = 427
follow_player = true
[node name="[Prop_0000]" type="Node" parent="Props/CameraAnchor"] [node name="[Prop_0000]" type="Node" parent="Props/CameraAnchor"]
script = ExtResource("9_fdfto") script = ExtResource("9_fdfto")

View File

@ -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_color: Color = Color(0.9, 0.3, 0.3, 0.8)
@export var limit_preview_line_width: float = 2.0 @export var limit_preview_line_width: float = 2.0
#@export_subgroup("Follow")
#@export var follow_player: bool = false
var _camera_global : Node = null var _camera_global : Node = null
var _priority: int : var _priority: int :
set(value): set(value):
if _priority != value: if _priority != value:
@ -85,15 +77,18 @@ func _ready() -> void:
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():
_runtime_ready() _runtime_ready()
func _runtime_ready() -> void:
_priority = priority
func _enter_tree() -> void: func _enter_tree() -> void:
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():
pass pass
#CameraSystem.register_anchor(self) #_camera_global.register_anchor(self) #只有在runtime我们才向管理器注册anchor
func _exit_tree() -> void: func _exit_tree() -> void:
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():
pass pass
#CameraSystem.unregister_anchor(self) #_camera_global.unregister_anchor(self) #只有在runtime我们才向管理器注册anchor
func _draw() -> void: func _draw() -> void:
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():
@ -144,8 +139,6 @@ func _camera_frame_rect_draw() -> Rect2:
print("沒有找到全局相機") print("沒有找到全局相機")
return Rect2(Vector2.ZERO,Vector2.ZERO) return Rect2(Vector2.ZERO,Vector2.ZERO)
func _runtime_ready() -> void:
_priority = priority
##内部懶加載全局相機管理器 ##内部懶加載全局相機管理器
func _get_camera_global() -> Object: func _get_camera_global() -> Object:
@ -157,9 +150,10 @@ func _get_camera_global() -> Object:
return _camera_global return _camera_global
## 强制清空所有的camera anchor in register 然后把自己注册进去
func push_camera() -> void: func push_camera() -> void:
#CameraSystem.reset_all_camera_priority() _get_camera_global().clear_camera_anchors()
_priority = 1000 _get_camera_global().register_anchor(self)
func pop_camera() -> void: func pop_camera() -> void:
_priority = 0 _get_camera_global().unregister_anchor(self)

View File

@ -10,7 +10,12 @@ var _runtime_registered := false
var _warned := false var _warned := false
var _final_offset := Vector2.ZERO 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: func _enter_tree() -> void:
if Engine.is_editor_hint(): if Engine.is_editor_hint():
@ -29,32 +34,70 @@ func _process(delta: float) -> void:
if not _camera: if not _camera:
return return
_update_base_position() _reset_dirty_flags()
_update_camera_offset()
_apply_camera_transform()
func _update_base_position(): _update_final_position()
_update_camera_offset()
_update_camera_zoom()
_apply_camera_config()
func _reset_dirty_flags() -> void:
_pos_dirty = false
_offset_dirty = false
_zoom_dirty = false
func _update_final_position():
for child in get_children(): for child in get_children():
if child.has_method("get_base_position"): if child is CameraToolBasic \
_base_position = child.get_base_position() and child.enabled \
and child.affect_position \
and child.has_base_position():
_final_position = child.get_base_position()
_pos_dirty = true
return return
# fallback # fallback
_base_position = _camera.global_position _final_position = _camera.global_position
func _update_camera_offset(): func _update_camera_offset():
var offset := Vector2.ZERO var offset := Vector2.ZERO
var used := false
for child in get_children(): 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() offset += child.get_camera_offset()
used = true
_final_offset = offset _final_offset = offset
_offset_dirty = used
func _apply_camera_transform(): func _update_camera_zoom():
_camera.global_position = _base_position 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 _camera.offset = _final_offset
if _zoom_dirty:
_camera.zoom = _final_zoom
func _notification(what: int) -> void: func _notification(what: int) -> void:
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():

View File

@ -9,8 +9,7 @@ var _camera_points : Array[CameraPointer]
var _current_camera_point : CameraPointer var _current_camera_point : CameraPointer
signal anchor_registered(anchor: CameraAnchor) signal anchors_changed
signal anchor_unregistered(anchor: CameraAnchor)
var _anchors : Array[CameraAnchor] var _anchors : Array[CameraAnchor]
@ -50,16 +49,32 @@ func get_camera() -> Camera2D:
func register_anchor(anchor: CameraAnchor) -> void: func register_anchor(anchor: CameraAnchor) -> void:
if anchor in _anchors: if anchor in _anchors:
return return
_anchors.append(anchor) _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: func unregister_anchor(anchor: CameraAnchor) -> void:
if _anchors.has(anchor): if anchor in _anchors:
_anchors.erase(anchor) _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]: func get_all_anchors() -> Array[CameraAnchor]:
return _anchors.duplicate() return _anchors.duplicate()
func clear_camera_anchors() -> void:
_anchors.clear()
anchors_changed.emit()
#endregion #endregion
func _enter_tree() -> void: func _enter_tree() -> void:

View File

@ -1,4 +1,4 @@
extends Node extends CameraToolBasic
class_name ReedCameraAnchorController class_name ReedCameraAnchorController
const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd") 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 _anchors: Array[CameraAnchor] = []
var _current_anchor: CameraAnchor var _current_anchor: CameraAnchor
var _current_position := Vector2.ZERO var _current_position := Vector2.ZERO
var _current_zoom := Vector2.ONE
var _tween: Tween var _tween: Tween
## ========================= ## =========================
## Config ## Config
## ========================= ## =========================
@export var enabled := true
@export var move_duration := 0.4 @export var move_duration := 0.4
@export var ease_type := Tween.EASE_OUT @export var ease_type := Tween.EASE_OUT
@export var trans_type := Tween.TRANS_CUBIC @export var trans_type := Tween.TRANS_CUBIC
## ========================= var _evaluate_requested := false
## Runtime State var _evaluate_scheduled := false
## =========================
#var _current_position: Vector2 var _sys : Object = null
var _target_position: Vector2
#var _tween: Tween enum AnchorState {
var _has_target := false IDLE,
TRANSITIONING,
}
var _state := AnchorState.IDLE
## ========================= ## =========================
## Lifecycle ## Lifecycle
## ========================= ## =========================
func _ready() -> void: func _ready() -> void:
var sys := Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME) _anchors = _get_camera_system().get_all_anchors()
if not sys:
_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 return
_anchors = sys.get_all_anchors() _evaluate_scheduled = true
call_deferred("_commit_evaluate")
sys.anchor_registered.connect(_on_anchor_added) func _commit_evaluate() -> void:
sys.anchor_unregistered.connect(_on_anchor_removed) _evaluate_scheduled = false
_evaluate() if not _evaluate_requested:
return
func _on_anchor_added(anchor: CameraAnchor) -> void: _evaluate_requested = false
_anchors.append(anchor) _evaluate_impl()
_evaluate()
func _on_anchor_removed(anchor: CameraAnchor) -> void: func _evaluate_impl() -> void:
_anchors.erase(anchor)
if _current_anchor == anchor:
_current_anchor = null
_evaluate()
func _evaluate() -> void:
var winner := _pick_best_anchor() var winner := _pick_best_anchor()
if winner == _current_anchor: if winner == _current_anchor:
return return
@ -78,22 +91,56 @@ func _switch_to(anchor: CameraAnchor) -> void:
return return
_current_anchor = anchor _current_anchor = anchor
_state = AnchorState.TRANSITIONING
var target := anchor.global_position 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(): if _tween and _tween.is_running():
_tween.kill() _tween.kill()
if not anchor.use_blend or anchor.blend_time <= 0.0: if not anchor.use_blend or anchor.blend_time <= 0.0:
_current_position = target _current_position = target
_current_zoom = target_zoom
_state = AnchorState.IDLE
return return
_tween = get_tree().create_tween() _tween = get_tree().create_tween()
_tween.set_trans(Tween.TRANS_CUBIC) _tween.set_ignore_time_scale(true)
_tween.set_ease(Tween.EASE_OUT) _tween.set_trans(trans_type)
_tween.set_ease(ease_type)
_tween.tween_property(self, "_current_position", target, anchor.blend_time) _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 ## CameraPointer Interface
## ========================= ## =========================
func get_base_position() -> Vector2: func get_base_position() -> Vector2:
if _state != AnchorState.TRANSITIONING:
return Vector2.ZERO
return _current_position 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

View File

@ -1,12 +1,6 @@
@tool @tool
extends Node extends CameraToolBasic
class_name ReedCameraShakeController class_name ReedCameraShakeController
## =========================
## Config
## =========================
@export var enabled := true
## ========================= ## =========================
## Runtime ## Runtime
## ========================= ## =========================

View File

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

View File

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

View File

@ -24,6 +24,8 @@ ReedCameraSystem="*res://addons/reedcamera/scripts/ReedCameraGlobal.gd"
[display] [display]
window/size/viewport_width=640
window/size/viewport_height=360
window/size/window_width_override=1920 window/size/window_width_override=1920
window/size/window_height_override=1080 window/size/window_height_override=1080
window/stretch/mode="canvas_items" window/stretch/mode="canvas_items"