godot-plateformer/addons/reedcamera/scripts/CameraPointer.gd

208 lines
4.4 KiB
GDScript3
Raw Normal View History

2026-01-12 17:51:15 +08:00
@tool
extends Node
class_name CameraPointer
const _CONSTANTS := preload("res://addons/reedcamera/_data/CameraSystemConst.gd")
2026-01-13 16:52:45 +08:00
## 相机指针所能持有的工具种类
enum ToolType{
SHAKER,
ANCHOR,
FOLLOWER
}
## 相机工具和脚本的映射
const type_script_map:Dictionary = {
ToolType.SHAKER : preload("res://addons/reedcamera/scripts/camera_tools/CameraShakeController.gd"),
ToolType.ANCHOR : preload("res://addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd"),
ToolType.FOLLOWER : preload("res://addons/reedcamera/scripts/camera_tools/CameraFollowController.gd")
}
2026-01-12 17:51:15 +08:00
var _camera: Camera2D
var _editor_valid := false
var _runtime_registered := false
var _warned := false
var _final_offset := Vector2.ZERO
2026-01-13 00:52:36 +08:00
var _final_position: Vector2
var _final_zoom := Vector2.ONE
var _pos_dirty := false
var _offset_dirty := false
var _zoom_dirty := false
2026-01-12 17:51:15 +08:00
2026-01-13 16:52:45 +08:00
2026-01-12 17:51:15 +08:00
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
2026-01-13 00:52:36 +08:00
_reset_dirty_flags()
_update_final_position()
2026-01-12 17:51:15 +08:00
_update_camera_offset()
2026-01-13 00:52:36 +08:00
_update_camera_zoom()
_apply_camera_config()
2026-01-12 17:51:15 +08:00
2026-01-13 00:52:36 +08:00
func _reset_dirty_flags() -> void:
_pos_dirty = false
_offset_dirty = false
_zoom_dirty = false
func _update_final_position():
2026-01-12 17:51:15 +08:00
for child in get_children():
2026-01-13 00:52:36 +08:00
if child is CameraToolBasic \
and child.enabled \
and child.affect_position \
and child.has_base_position():
_final_position = child.get_base_position()
_pos_dirty = true
2026-01-12 17:51:15 +08:00
return
# fallback
2026-01-13 00:52:36 +08:00
_final_position = _camera.global_position
2026-01-12 17:51:15 +08:00
func _update_camera_offset():
var offset := Vector2.ZERO
2026-01-13 00:52:36 +08:00
var used := false
2026-01-12 17:51:15 +08:00
for child in get_children():
2026-01-13 00:52:36 +08:00
if child is CameraToolBasic \
and child.enabled \
and child.affect_offset \
and child.has_camera_offset():
2026-01-12 17:51:15 +08:00
offset += child.get_camera_offset()
2026-01-13 00:52:36 +08:00
used = true
2026-01-12 17:51:15 +08:00
_final_offset = offset
2026-01-13 00:52:36 +08:00
_offset_dirty = used
func _update_camera_zoom():
for child in get_children():
if child is CameraToolBasic \
and child.enabled \
and child.affect_zoom \
and child.has_camera_zoom():
_final_zoom = child.get_camera_zoom()
_zoom_dirty = true
return
# fallback
_final_zoom = _camera.zoom
func _apply_camera_config():
if _pos_dirty:
_camera.global_position = _final_position
if _offset_dirty:
_camera.offset = _final_offset
2026-01-12 17:51:15 +08:00
2026-01-13 00:52:36 +08:00
if _zoom_dirty:
_camera.zoom = _final_zoom
2026-01-12 17:51:15 +08:00
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
2026-01-13 16:52:45 +08:00
2026-01-12 17:51:15 +08:00
if not _camera:
2026-01-13 16:52:45 +08:00
_camera = _find_camera()
2026-01-12 17:51:15 +08:00
_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
2026-01-13 10:32:36 +08:00
## =========================
## Public API
## =========================
##获取相机
2026-01-12 17:51:15 +08:00
func get_camera() -> Camera2D:
2026-01-13 16:52:45 +08:00
if not _camera:
_camera = _find_camera()
2026-01-12 17:51:15 +08:00
return _camera
2026-01-13 10:32:36 +08:00
##获取特定工具
func get_tool(tool_class: Script) -> Node:
for c in get_children():
if c.get_script() == tool_class:
return c
return null
2026-01-13 16:52:45 +08:00
##通过类型返回工具
func get_tool_by_type(type:ToolType) -> Node:
return get_tool(type_script_map.get(type))
2026-01-13 10:32:36 +08:00
##获取所有工具
func get_tools() -> Array[CameraToolBasic]:
var out: Array[CameraToolBasic] = []
for c in get_children():
if c is CameraToolBasic:
out.append(c)
return out