WIP: refactor camera anchor system
This commit is contained in:
parent
97ea4ae12e
commit
98178a2bd2
|
|
@ -4,7 +4,7 @@
|
||||||
#
|
#
|
||||||
#'''
|
#'''
|
||||||
#@tool
|
#@tool
|
||||||
#extends Node
|
extends Node
|
||||||
#
|
#
|
||||||
#@onready var camera_2d: Camera2D = %Camera2D
|
#@onready var camera_2d: Camera2D = %Camera2D
|
||||||
#@onready var camera_shake_player: CameraShakePlayer = %CameraShakePlayer
|
#@onready var camera_shake_player: CameraShakePlayer = %CameraShakePlayer
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
extends RemoteTransform2D
|
extends RemoteTransform2D
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
var global_camera: Camera2D = CameraSystem.get_cached_camera()
|
pass
|
||||||
if not global_camera:
|
#var global_camera: Camera2D = CameraSystem.get_cached_camera()
|
||||||
push_error("[CameraFollower]:No Global Camera Founded")
|
#if not global_camera:
|
||||||
return
|
#push_error("[CameraFollower]:No Global Camera Founded")
|
||||||
remote_path = global_camera.get_path()
|
#return
|
||||||
|
#remote_path = global_camera.get_path()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://djk7tg2puphgv
|
||||||
|
|
@ -78,6 +78,7 @@ unique_name_in_owner = true
|
||||||
shape = SubResource("RectangleShape2D_qnulu")
|
shape = SubResource("RectangleShape2D_qnulu")
|
||||||
|
|
||||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||||
|
visible = false
|
||||||
texture_filter = 1
|
texture_filter = 1
|
||||||
position = Vector2(0, -2)
|
position = Vector2(0, -2)
|
||||||
texture = SubResource("AtlasTexture_basl5")
|
texture = SubResource("AtlasTexture_basl5")
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
@ -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"]
|
||||||
|
|
@ -44,8 +44,8 @@ func _enter() -> void:
|
||||||
elif i == 3 or i == 7:
|
elif i == 3 or i == 7:
|
||||||
csp = agent.camera_shake_preset.get("xy_light")
|
csp = agent.camera_shake_preset.get("xy_light")
|
||||||
|
|
||||||
if csp:
|
#if csp:
|
||||||
CameraSystem.camera_shake_player.play(csp)
|
#CameraSystem.camera_shake_player.play(csp)
|
||||||
|
|
||||||
if root.grap_hook_state._jump_grace_timer > 0:
|
if root.grap_hook_state._jump_grace_timer > 0:
|
||||||
_hook_to_jump()
|
_hook_to_jump()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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"
|
||||||
|
Before Width: | Height: | Size: 869 B After Width: | Height: | Size: 869 B |
|
|
@ -3,15 +3,15 @@
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bsdmq0essfmpk"
|
uid="uid://bsdmq0essfmpk"
|
||||||
path="res://.godot/imported/camera_anchor_icon.svg-bc0c9f7b183031f0db701d2e858a9063.ctex"
|
path="res://.godot/imported/camera_anchor_icon.svg-d54e4ec18108e371c1abc23152af31de.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://_asset/icon/camera_anchor_icon.svg"
|
source_file="res://addons/reedcamera/icon/camera_anchor_icon.svg"
|
||||||
dest_files=["res://.godot/imported/camera_anchor_icon.svg-bc0c9f7b183031f0db701d2e858a9063.ctex"]
|
dest_files=["res://.godot/imported/camera_anchor_icon.svg-d54e4ec18108e371c1abc23152af31de.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://5xuwd0cde3y8
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://wlqopoksgvjc
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
##TODO:清楚掉這裏和PhantomCamera相關的部分
|
##TODO:清楚掉這裏和PhantomCamera相關的部分
|
||||||
@tool
|
@tool
|
||||||
@icon("uid://bsdmq0essfmpk")
|
@icon("res://addons/reedcamera/icon/camera_anchor_icon.svg")
|
||||||
class_name CameraAnchor extends Node2D
|
class_name CameraAnchor extends Node2D
|
||||||
|
|
||||||
const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd")
|
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
|
@export var priority: int = 0
|
||||||
##此Anchor是否有效
|
##此Anchor是否有效
|
||||||
@export var enabled: bool = true
|
@export var enabled: bool = true
|
||||||
|
## =========================
|
||||||
|
## Blend Config
|
||||||
|
## =========================
|
||||||
|
@export_subgroup("Blending")
|
||||||
##是否要存在相機過渡時間
|
##是否要存在相機過渡時間
|
||||||
@export var use_blend: bool = true
|
@export var use_blend: bool = true
|
||||||
##過度時間
|
##過度時間
|
||||||
|
|
@ -49,12 +53,6 @@ const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd")
|
||||||
limit_right = value
|
limit_right = value
|
||||||
if Engine.is_editor_hint():
|
if Engine.is_editor_hint():
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
@export_subgroup("Follow")
|
|
||||||
@export var follow_player: bool = false
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var _camera_global : Node = null
|
|
||||||
|
|
||||||
## 编辑器预览面板设置
|
## 编辑器预览面板设置
|
||||||
@export_group("Editor Preview")
|
@export_group("Editor Preview")
|
||||||
|
|
@ -66,6 +64,15 @@ var _camera_global : Node = null
|
||||||
@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 _priority: int :
|
var _priority: int :
|
||||||
set(value):
|
set(value):
|
||||||
if _priority != value:
|
if _priority != value:
|
||||||
|
|
@ -147,7 +154,6 @@ func _get_camera_global() -> Object:
|
||||||
|
|
||||||
if Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME):
|
if Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME):
|
||||||
_camera_global = Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME)
|
_camera_global = Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME)
|
||||||
print("")
|
|
||||||
|
|
||||||
return _camera_global
|
return _camera_global
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://py4h5jxlncro
|
||||||
|
|
@ -5,6 +5,63 @@ const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd")
|
||||||
|
|
||||||
var _screen_size : Vector2i
|
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:
|
func _enter_tree() -> void:
|
||||||
if not Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME):
|
if not Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME):
|
||||||
Engine.register_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME, self)
|
Engine.register_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME, self)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://bhl5it46hv4n2
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
uid://dwr1s51svvank
|
||||||
|
|
@ -17,16 +17,13 @@ config/icon="res://icon.svg"
|
||||||
|
|
||||||
[autoload]
|
[autoload]
|
||||||
|
|
||||||
ReedCameraSystem="*res://addons/reedcamera/scripts/ReedCameraGlobal.gd"
|
|
||||||
CameraSystem="*res://_camera/CameraSystem.tscn"
|
|
||||||
GlobalEvent="*res://_shared/GlobalEvent.gd"
|
GlobalEvent="*res://_shared/GlobalEvent.gd"
|
||||||
ReedVFX="*res://addons/reedfx/vfx/ReedVFXSystem.tscn"
|
ReedVFX="*res://addons/reedfx/vfx/ReedVFXSystem.tscn"
|
||||||
ReedSceneRegistry="*res://addons/reedscene/scene/SceneRegistry.gd"
|
ReedSceneRegistry="*res://addons/reedscene/scene/SceneRegistry.gd"
|
||||||
|
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"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue