2D camera that zooms inteligently

Godot Version: v4.3.stable.official [77dcf97d8]

When I look up the term, the internet says I’m looking for intelligent zooming. I am still quite new to GDscript, otherwise I think I’d be able to do something with a video I’ve found where the guy does what I’m looking for but in 3d.
I have zoom working, it increases and decreases the zoom when scrolling the mouse wheel, but I’d like the zoom to be relative to the cursor’s current location, like when you’re using a graphic program, or other such programs.

If it helps I’ll put the script I use for zooming, and maybe if someone has something I can plug in to make it do, that’d be nice. I’d also appreciate a description of why the code works the way it does, as I’m always interested in that sort of thing. So far I’ve been able to pick up most things by watching people use the code, and then sort of fitting it to my purpose when needed, but in this case the only person I’ve seen doing exactly what I’m looking for is doing it in 3d, and in a video from 4 years ago.

This is my full camera script, so far, I don’t know why it’s not all in the box:

Edit: added that bit about the box, but also I wanted to let you know my camera is in its own node, and works great for what I can get it to do so far, just by dragging it into the node I need it and using it. I have a canvas layer that I attatch to it, for the esc menu, which comes up at the top right, but only when it’s full screen in the proper resolution (one day I’ll find the video I found that would let me fix that) and I have no canvas layer that it’s nested into or anything, it’s just child to the main node2d that runs the main scene. I had someone ask for an example of what I’m looking for, so I guess instead of putting a video I invite you to open godot right now, get into a scene with at least one image, then put your mouse on one side of the image, zoom in, then put it on the other side of the image and zoom out. THAT is what I’m looking to get going here. Nothing too extreme, just the little camera panning that happens when you zoom. The direct in and out that I have now is only acceptable for when I pin it to a 2d character boi, which it works great for btw. (I have seriously considered somehow making a 2d npc that follows my mouse, but only when I zoom, that I can then have hover on the camera, so it stays relative somehow to the camera, but also not? it’d be a very stupid way to solve this, so I don’t want that. he’d also be invisible!)

Edit 2: Here is the script I’m using now. it’s a bit of a frankensmonster of different things I’ve grouped together, but it does what I want to do, even if it’s not quite as smooth as I’d like yet.

Edit 3: Figured out how to put code in!

had to fix the stuff with astrisks not showing up ><

extends Camera2D


var zoomSpd: float = 0.05*zoom.y
var Minzoom: float = 0.1
var Maxzoom: float = 1.0
var dragSen: float = 1.0
var spd = zoomSpd*3.5*zoom.y*zoom.y ##comfortable zoom/pan speed still a bit jagged.



func _input(event):
   var mouse_position = get_global_mouse_position()
   var mouse_delta = mouse_position - global_position

   if event is InputEventMouseMotion and Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
   	position -= event.relative * (dragSen/zoom.y)#pretty sure this last bit is the way you get it to scale.

   if event is InputEventMouseButton: #for zooming
   	if event.button_index == MOUSE_BUTTON_WHEEL_UP and zoom.y!=Maxzoom:
   		zoom += Vector2(zoomSpd,zoomSpd)
   		position += mouse_delta * spd
   	elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
   		zoom -= Vector2(zoomSpd,zoomSpd)
   		if zoom.y>Minzoom:
   			position -= mouse_delta * spd 

   	zoom = clamp(zoom, Vector2(Minzoom, Minzoom), Vector2(Maxzoom, Maxzoom)) #Limits the zooming

rely on CanvasItem.get_global_mouse_position.
Local position isn’t a thing when any camera+zoom used.
Idea is pretty simple -
current_global_cam_position=lerp(current_global_cam_position, global_mouse position, 0.5)
I think thats what you’re looking

2 Likes

I’m still pretty new to all of this, and the code does seem to make some sense, but I don’t think I grasp the context. so far I’m finding it hard finding the context for a lot of the documentation. I assume it will all click soon.
can you be more clear how to implement the came positioning, I am sure that’s what I’m looking for in this case, because in the simplest terms, the camera moving is what I’m seeking.

var move_camera:=false
@onready var camera:=$Node/target/Camera2D
func _unhandled_input(e: InputEvent) -> void:
	if e is InputEventMouseButton:
		if e.pressed:
			if e.button_index==3: move_camera=true
			if e.button_index==4:
				move_to_mouse_pos(camera,1.0)
			elif e.button_index==5:
				move_to_mouse_pos(camera,-1.0)
		elif not e.pressed and e.button_index==3: move_camera=false
	if e is InputEventMouseMotion and move_camera:
		camera.position+=-e.relative/camera.zoom.x
func move_to_mouse_pos(cam:Camera2D, cam_zoom:float)->void:
	cam.global_position=lerp(cam.get_global_mouse_position(), cam.global_position, 0.75)
	cam.zoom*=1.0+(0.1*cam_zoom)

a ready script, moves on middle mouse button pressed
I recommend to give all these buttons action names and use _input

2 Likes

Well, using it as is is throwing errors, both with and without the original script I’ve been using. Weirdly it’s the same errors the both times. using the middle click actually throws a different error than the scrolling, but both in and out both give me "cannot call method ‘get_global_mouse_position’ on a null value. which is actually the error I was running into when I was trying to use just the first bit you sent in the script.

In the mean time I will see if I can’t rip apart what you’ve got here to see if any of it can be plugged into the zooming I have working, but I suspect that error I’m getting is going to be a bit of a hurdle.

I’m grateful for your responses, and I’ve edited the original post to add some additional context that may be useful.

set camera property to valid camera node path (drag it from scene tree)

1 Like

I had such an issue trying to figure out what you mean here, but I realize now you mean to link it into the script with the thingy, like how they do the onreadys and such.

Well, the actual movement is a tiny bit wonky, but I’ll probably be able to adjust that. It’s working though, and I’m happy about that.

Thank you very much. Now I get to tweak the numbers and get this more smooth.

Been toying with it a bit, the smoothing is being a trouble, but I’ve run into a different issue. zooming in, and out, both bring the camera in the direction of the cursor (Very fast, still trying to figure out how to slow that down) but it should be going away from the cursor when zooming out, because that’s the logic that we see in other zooming situations, but nothing I can think of seems to want to just reverse that, and I’ve been just coming up empty over and over.
Got any ideas?

change line:

cam.global_position=lerp(cam.get_global_mouse_position(), cam.global_position, 0.75)

to

if cam_zoom>0: cam.global_position=lerp(cam.get_global_mouse_position(), cam.global_position, 0.75)

if I understood what you meant.

to tweak a speed of zoom, change 1.0+(0.1 * cam_zoom) to lower or greater.
cam offsetcould be decreased a little if change value 0.75+ (max 1.0, no offset)

1 Like

So, replacing the line with the other one actually just made so zooming out does nothing, by way of movement, anyway.

I realized as I was tinkering with the biz, that when I initially set up the hold to move stuff, it had you scale the speed it moves to how zoomed you are, and it seems none of the answers to this question do that, so I will have to see about adding something into it to do that as well.

I’m half tempted to just get into godots own script, since the zoom function in the visual bit of the 2d editor works perfectly for what I want, and I’m led to believe it’s also made in gdscript.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.