Cyclone-Tracker/addons/Fantail-Interactive.top_down_camera/td_camera_2d.gd
2025-06-29 16:25:51 +10:00

129 lines
4.2 KiB
GDScript

extends Camera2D
## Scroll by using ui direction actions
@export var keyboard_pan := true
@export_group("Keyboard Settings")
@export var move_left_action := "ui_left"
@export var move_right_action := "ui_right"
@export var move_up_action := "ui_up"
@export var move_down_action := "ui_down"
@export_group("")
## Scroll by clicking right mouse button
@export var mouse_drag_pan := true
@export_group("Drag Settings")
@export var drag_action := "drag_camera"
@export_group("")
## Scroll by moving mouse to the window edge
@export var mouse_edge_pan := false
@export var use_mouse_wheel := true
@export_group("Zoom Settings")
@export var min_zoom := 2.0
@export var max_zoom := 0.5
@export var zoom_speed := 0.1
@export var zoom_in_action := "zoom_in"
@export var zoom_out_action := "zoom_out"
@export_group("")
## How close should the mouse cursor be to the window edge (in pixels) before it starts
## to pan the camera
@export var edge_margin := 50
## Camera speed (pixels/s)
@export var pan_speed := 450
var prev_mouse_pos := Vector2.ZERO
var dragging := false
var move_left := false
var move_right := false
var move_up := false
var move_down := false
func _ready() -> void:
# We want the camera to move when we set position
set_drag_horizontal_enabled(false)
set_drag_vertical_enabled(false)
## Check for events detected by _unhandled_input and handle them
func _process(delta: float) -> void:
var motion := Vector2.ZERO
if keyboard_pan:
if move_left:
motion += Vector2.LEFT
if move_up:
motion += Vector2.UP
if move_right:
motion += Vector2.RIGHT
if move_down:
motion += Vector2.DOWN
var mouse_pos := get_local_mouse_position()
# Pan camera when the mouse has been moved to the window edges
if mouse_edge_pan:
var rect := get_viewport().get_visible_rect()
var margin_rect = rect.grow(-edge_margin)
margin_rect.position -= rect.size / 2
if not margin_rect.has_point(mouse_pos):
motion += margin_rect.get_center().direction_to(mouse_pos)
motion *= (pan_speed * (1 / zoom.x)) * delta / Engine.time_scale
if mouse_drag_pan and dragging:
motion = prev_mouse_pos - mouse_pos
# Update position of the camera.
position += motion
clamp_position_to_limits()
if use_mouse_wheel:
if Input.is_action_just_released(zoom_in_action):
zoom += Vector2(zoom_speed, zoom_speed)
if Input.is_action_just_released(zoom_out_action):
zoom -= Vector2(zoom_speed, zoom_speed)
zoom.x = clampf(zoom.x, max_zoom, min_zoom)
zoom.y = zoom.x
prev_mouse_pos = get_local_mouse_position()
## Check for any unhandled input events and take note of them
##
## We are taking press/release events and turning them into a flag of whether the
## button is being held down
##
## NOTE: This is done here to ensure that we only consume input events if no other node does.
## i.e. the camera won't move if the player is moving through menus
func _unhandled_input(event: InputEvent) -> void:
# TODO: Change to input action
#print(event)
if event.is_action(drag_action):
dragging = event.is_pressed()
return
# Transform a key change event into a "pressed" flag.
# We do it this way because we're checking against an InputEvent, not Input
if event.is_action_pressed(move_left_action):
move_left = true
if event.is_action_pressed(move_up_action):
move_up = true
if event.is_action_pressed(move_right_action):
move_right = true
if event.is_action_pressed(move_down_action):
move_down = true
if event.is_action_released(move_left_action):
move_left = false
if event.is_action_released(move_up_action):
move_up = false
if event.is_action_released(move_right_action):
move_right = false
if event.is_action_released(move_down_action):
move_down = false
## Clamp the camera position to within the camera limits
##
## This fixes an issue where the camera can get "stuck" when in corners. I assume this
## is due to stopping all movement when a limit is reached
func clamp_position_to_limits() -> void:
var view_size := get_viewport().get_visible_rect ().size * get_zoom().x
var half_width := view_size.x / 2.0
var half_height := view_size.y / 2.0
position.x = clampf(position.x, limit_left + half_width, limit_right - half_width)
position.y = clampf(position.y, limit_top + half_height, limit_bottom - half_height)