godot-plateformer/addons/reedcamera/scripts/camera_tools/CameraFollowController.gd

207 lines
5.3 KiB
GDScript
Raw Permalink 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 CameraToolBasic
class_name ReedCameraFollowController
const _CONSTANTS := preload("res://addons/reedcamera/_data/CameraSystemConst.gd")
const _DEBUG_TOOL := preload("res://addons/reedcamera/scripts/camera_tools/DeadZoneDebug.tscn")
@export_group("Dead Zone")
@export var dead_zone_ratio : Vector2 = Vector2(.6,.6)
@export_group("Follow")
@export var enabled_follow: bool = true
@export var follow_speed: float = 10000.0 # 世界单位 / 秒
@export var follow_lerp := 0.12 # 0~1越大越“跟手”越小越“蔚蓝感”的滞后
@export_subgroup("Follow Dynamic Speed")
@export var min_speed_scale := 0.4 # 贴近死区时
@export var max_speed_scale := 2.0 # 远离死区时
@export var max_offset_ratio := 1.0 # offset 达到 1 个 dead_zone 时为最大速率
##TODO:后续添加一下Runtime的修改逻辑
@export_group("Debug")
@export var show_preview: bool = true
var _show_preview: bool = false:
set(value):
_update_debug(value)
_show_preview = value
@export var preview_color: Color = Color.AQUAMARINE
@export var preview_line_width : float = 2.0
enum State{
IDLE,
CHASING,
STATIC
}
## 用于描述和切换follower工具的状态
var _current_state: State = State.IDLE
##Follower工具绑定的followNode
var _follow_node2d : Node2D = null
##最终的位置
var _final_position: Vector2
##缓存全局系统
var _sys: Object = null
##缓存DebugTool
var _screen_ratio_rect_draw_tool: Node = null
##缓存camera
var _camera: Camera2D = null
func _ready() -> void:
_show_preview = show_preview
func _process(delta: float) -> void:
#print(_current_state)
if _current_state == State.IDLE:
return
if _current_state == State.CHASING or State.STATIC:
update_follow(delta)
func _update_debug(debug:bool) -> void:
if debug:
if _screen_ratio_rect_draw_tool:
var t := _screen_ratio_rect_draw_tool.get_child(0)
t.set_ratio(dead_zone_ratio)
t.set_target_node(_follow_node2d)
t.set_style(preview_color,preview_line_width)
return
else:
var t := _DEBUG_TOOL.instantiate()
self.add_child(t)
_screen_ratio_rect_draw_tool = t
t.get_child(0).set_ratio(dead_zone_ratio)
t.get_child(0).set_target_node(_follow_node2d)
t.get_child(0).set_style(preview_color,preview_line_width)
else:
if not _screen_ratio_rect_draw_tool:
return
else:
_screen_ratio_rect_draw_tool.get_child(0).clear()
func update_follow(delta: float) -> void:
if not enabled_follow:
return
if not _follow_node2d:
return
var cam := _get_camera_from_pointer()
if not cam:
return
# 0. 读取相机的全局坐标
_final_position = cam.global_position
# 1. target 的屏幕坐标Canvas Space
var t_in_screen :Vector2 = \
_follow_node2d.get_global_transform_with_canvas().get_origin()
var screen_size : Vector2 = _get_camera_system().get_screen_size()
var screen_center : Vector2 = screen_size * 0.5
var dead_half : Vector2 = screen_size * dead_zone_ratio * 0.5
# 2. 获得移动方向
var offset := \
_compute_deadzone_offset(
t_in_screen,
screen_center,
dead_half
)
if offset == Vector2.ZERO:
_current_state = State.STATIC
return
_current_state = State.CHASING
#var move_dir := offset.normalized()
#var world_velocity := (move_dir * follow_speed) / cam.zoom
var offset_len := offset.length()
var dead_len := dead_half.length()
# 0~1 的距离比例
var t := clamp(offset_len / (dead_len * max_offset_ratio), 0.0, 1.0)
# 距离越远,速度越快
var speed_scale := lerp(min_speed_scale, max_speed_scale, t)
var move_dir := offset.normalized()
var world_velocity : Vector2 = (move_dir * follow_speed * speed_scale) / cam.zoom
var desired_pos := _final_position + world_velocity * delta
if follow_lerp > 0.0:
_final_position = cam.global_position.lerp(desired_pos, follow_lerp)
else:
_final_position = desired_pos
func _compute_deadzone_offset(
screen_pos: Vector2,
screen_center: Vector2,
dead_half: Vector2
) -> Vector2:
var o := Vector2.ZERO
var dx := screen_pos.x - screen_center.x
var dy := screen_pos.y - screen_center.y
if dx < -dead_half.x:
o.x = dx + dead_half.x
elif dx > dead_half.x:
o.x = dx - dead_half.x
if dy < -dead_half.y:
o.y = dy + dead_half.y
elif dy > dead_half.y:
o.y = dy - dead_half.y
return o
## 从相机指针获取相机组件
func _get_camera_from_pointer() -> Camera2D:
if _camera:
return _camera
var p := get_parent()
if p and p.has_method("get_camera"):
_camera = p.get_camera() as Camera2D
return _camera
return _camera
## 获取全局相机管理器
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
##外部调用,设置一个相机系统的跟随者
func register_follower(follower: Node2D) -> void:
_follow_node2d = follower
if self._current_state == State.IDLE:
start_follow()
##外部调用,移除一共相机系统的跟随者
func unregister_follower() -> void:
_follow_node2d = null
stop_follow()
##外部调用,开始跟随
func start_follow() -> void:
_current_state = State.STATIC
##外部调用,结束跟随
func stop_follow() -> void:
_current_state = State.IDLE
func get_base_position() -> Vector2:
return _final_position
func has_base_position() -> bool:
return _current_state == State.CHASING