When trying to create a controller for Camera2D I needed to make it so the camera is going to zoom to the current cursor position.
The expected behavior
When I scroll the wheel the camera will zoom in/out and the point just under the cursor will not offset relative to cursor.
The problem
After the first tries, learning and searching the answers on the forum and reddit I thought that I needed to capture the cursor position before and after zooming. Then I need to offset the camera position by the difference between the old and new cursor positions.
This is the script that I came up with:
extends Camera2D
var zoom_step: float = 0.05
var zoom_min: float = 0.05
var zoom_max: float = 10.0
func _input(event: InputEvent) -> void:
if event.is_action(&"zoom_in"):
zoom_camera(1.0 + zoom_step)
if event.is_action(&"zoom_out"):
zoom_camera(1.0 - zoom_step)
func zoom_camera(zoom_factor: float) -> void:
var new_zoom: Vector2 = (zoom * zoom_factor)
new_zoom = new_zoom.clamp(
Vector2(zoom_min, zoom_min),
Vector2(zoom_max, zoom_max)
)
var mouse_position: Vector2 = get_global_mouse_position()
zoom = new_zoom
var new_mouse_position: Vector2 = get_global_mouse_position()
global_position += mouse_position - new_mouse_position
However the desired behavior isn't satisfied by this approach because after offsetting the camera by the difference of 2 cursor positions the point under the cursor is being offset after zooming.
:(
For the context, for the testing I created a scene with only Node2D as a root, a Camera2D and a couple of Sprite2D nodes for zooming reference.
I tried to debug the scene by placing 2 Polygons2D and changing the global_position of the first to correspond mouse_position value and the second to correspond the new_mouse_position value. This didn't get me too far, I just got a confirmation that something is wrong as I saw how their positions differ.
I’m not sure how to debug this and my only guess is that Camera2D position and/or offset is going through some changes made by viewport or something else.
Thank you for your efforts. Your solution doesn’t satisfy the problem, however.
That’s because when I zoom in/out the point under the cursor is being offset and then the camera position is being adjusted further in the _process function. This means when I zoom in/out 2 times in a short amount of time the camera will be corrected to the last target_position only.
Well, that’s the behavior of the godot’s 2d editor camera. The default behavior of Camera2D zooming is that it is zooming to the center of the view. The camera inside the editor is zooming to the current position of the cursor.
I’ll try to record a short example tomorrow if needed.
Yes, I tried to do so today as it was suggested by the documentation of the Camera2D. However I faced a similar problem essentially
For translating I affected viewport.canvas_transform.origin and for scaling I used viewport.canvas_transform.scaled() method.
I tried a different approach of first moving the viewport origin to cursor, scaling and then moving back to original position. Tried also multiplying/dividing the second offset by zoom, but everything I tried was not successful.
I’m basically back at step 1 at a time of writing my post here
extends Node2D
const scale_step := Vector2(1.1, 1.1)
@onready var vp := get_viewport()
func _input(e):
# pan
if e is InputEventMouseMotion:
if e.button_mask & MOUSE_BUTTON_MASK_MIDDLE:
vp.canvas_transform = vp.canvas_transform.translated(e.relative)
# zoom
if e is InputEventMouseButton and e.is_pressed():
if e.button_index != MOUSE_BUTTON_WHEEL_DOWN and e.button_index != MOUSE_BUTTON_WHEEL_UP:
return
vp.canvas_transform = vp.canvas_transform.translated(-vp.get_mouse_position())
if e.button_index == MOUSE_BUTTON_WHEEL_DOWN:
vp.canvas_transform = vp.canvas_transform.scaled(Vector2.ONE / scale_step)
if e.button_index == MOUSE_BUTTON_WHEEL_UP:
vp.canvas_transform = vp.canvas_transform.scaled(scale_step)
vp.canvas_transform = vp.canvas_transform.translated(vp.get_mouse_position())