class_name Camera extends Camera2D signal zoomed(zoom: Vector2) var is_in_drag_mode := false var drag_anchor := Vector2.ZERO var is_in_edge_mode := false var edge := Vector2.ZERO @export var edge_scroll_margin := 4.0 @export var speed := 25 @export_group("Zoom", "zoom") @export var zoom_min: float = 0.5 @export var zoom_max: float = 3 @export var zoom_step: float = 0.01 func _ready(): get_viewport().size_changed.connect(func(): set_new_position(global_position) var new_zoom = Vector2( (limit_right - limit_left) / get_viewport_rect().size.x, (limit_bottom - limit_top) / get_viewport_rect().size.y ) zoom = Vector2( new_zoom[new_zoom.max_axis_index()], new_zoom[new_zoom.max_axis_index()] ) zoomed.emit(zoom) ) Input.mouse_mode = Input.MOUSE_MODE_CONFINED func _input(event): if event.is_action("camera_zoom_out"): var can_zoom_out = ( (limit_right - limit_left) / get_rect().size.x > 1.0 and (limit_bottom - limit_top) / get_rect().size.y > 1.0 ) if can_zoom_out: var new_zoom = max(zoom.x - zoom_step, zoom_min) zoom = Vector2(new_zoom, new_zoom) zoomed.emit(zoom) if event.is_action("camera_zoom_in"): var new_zoom = min(zoom.x + zoom_step, zoom_max) zoom = Vector2(new_zoom, new_zoom) zoomed.emit(zoom) if event.is_action_pressed("camera_drag"): is_in_drag_mode = true drag_anchor = get_global_mouse_position() Input.set_default_cursor_shape(Input.CURSOR_MOVE) elif event.is_action_released("camera_drag"): is_in_drag_mode = false Input.set_default_cursor_shape(Input.CURSOR_ARROW) if event is InputEventMouseMotion: is_in_edge_mode = false edge = Vector2.ZERO if get_global_mouse_position().x - edge_scroll_margin <= get_rect().position.x: is_in_edge_mode = true edge += Vector2.LEFT if get_global_mouse_position().y - edge_scroll_margin <= get_rect().position.y: is_in_edge_mode = true edge += Vector2.UP if get_global_mouse_position().x + edge_scroll_margin >= get_rect().end.x: is_in_edge_mode = true edge += Vector2.RIGHT if get_global_mouse_position().y + edge_scroll_margin >= get_rect().end.y: is_in_edge_mode = true edge += Vector2.DOWN edge = edge.normalized() if event is InputEventMouseButton and event.is_pressed(): Input.mouse_mode = Input.MOUSE_MODE_CONFINED elif event.is_action_pressed("mouse_escape"): Input.mouse_mode = Input.MOUSE_MODE_VISIBLE func _process(delta): if is_in_drag_mode: set_new_position(global_position + drag_anchor - get_global_mouse_position()) if is_in_edge_mode: set_new_position( lerp( global_position, global_position + (edge * speed), speed * delta ) ) var direction_h = Input.get_axis("ui_left", "ui_right") var direction_v = Input.get_axis("ui_up", "ui_down") if direction_h != 0 or direction_v != 0: set_new_position( lerp( global_position, global_position + Vector2(direction_h * speed, direction_v * speed), speed * delta ) ) func set_new_position(new_position: Vector2): new_position.x = clamp( new_position.x, limit_left, limit_right - get_rect().size.x ) new_position.y = clamp( new_position.y, limit_top, limit_bottom - get_rect().size.y ) global_position = new_position func set_center(center_position: Vector2): set_new_position( center_position - (get_rect().size / 2) ) func get_rect(): var viewport = get_viewport_rect() return Rect2( global_position, viewport.size * Vector2(1 / zoom.x, 1 / zoom.y) )