godot-plateformer/_shared/camera/CameraSystem.gd

180 lines
4.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'''全局的相机管理器
======= 外部调用函数 =======
'''
extends Node
var _cached_player_camera: GlobalCamera
var _cached_anchors: Array[CameraAnchor] = []
var _current_anchor: CameraAnchor
var _switch_tween: Tween
##标记位,用来检测当前帧是否存在相机切换
var _switch_scheduled := false
var _dirty := false
## 玩家关卡内静态相机
const PLAYER_CAMERA_SCENE:= preload("res://_shared/camera/PlayerStaticCamera.tscn")
## 注册玩家相机
func register_player_camera(owner: Node) -> GlobalCamera:
if not _cached_player_camera:
_cached_player_camera = PLAYER_CAMERA_SCENE.instantiate() as GlobalCamera
if _cached_player_camera:
owner.add_child(_cached_player_camera)
return _cached_player_camera
## 外部获取玩家全局相机
func get_cached_camera() -> GlobalCamera:
return _cached_player_camera
func get_player_camera() -> GlobalCamera:
return _cached_player_camera
## 注册一个相机锚点
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()
#if anchor in _cached_anchors:
#return
#_cached_anchors.append(anchor)
#anchor.on_priority_change.connect(on_anchor_priority_changed)
#_sort_anchors()
#_try_auto_switch()
## 当相机锚点的权重改变时,向管理器触发事件
func on_anchor_priority_changed(priority:int, anchor: CameraAnchor) -> void:
_request_evaluate()
#if _current_anchor:
#if _current_anchor._priority < priority:
#_sort_anchors()
#_try_auto_switch()
## 注销一个相机锚点
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")
# call_deferred 会在当前调用栈/本帧末尾idle执行一次
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))
# 计算 winner只算一次
var winner: CameraAnchor = _pick_best_anchor()
# winner 为空就不动(或回默认)
if winner == null:
return
# 如果没变,不切
if winner == _current_anchor:
return
switch_anchor(winner)
#_cached_anchors.erase(anchor)
#if _current_anchor == anchor:
#_current_anchor = null
#_try_auto_switch()
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
## 切换相机
func switch_anchor(target_anchor: CameraAnchor) -> void:
if target_anchor == null:
return
if target_anchor == _current_anchor:
return
if not is_instance_valid(_cached_player_camera):
return
# 中断旧 Tween
if _switch_tween and _switch_tween.is_running():
_switch_tween.kill()
_switch_tween = null
var camera := _cached_player_camera
var p_camera : PhantomCamera2D = _cached_player_camera.phantom_camera_2d
var blend_time : float = max(target_anchor.blend_time, 0.001)
# 创建 Tween关键ignore time scale
_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(
camera,
"global_position",
target_anchor.global_position,
blend_time
)
# ===== Zoom =====
_switch_tween.parallel().tween_property(
p_camera,
"zoom",
target_anchor.zoom,
blend_time * 1.5
)
# 完成回调
_switch_tween.finished.connect(func():
_current_anchor = target_anchor
)
_current_anchor = target_anchor