Scale object while changing PivotOffset

Godot Version

Godot 4

Question

I’m trying to scale an object given some mouse input (wheel up/down) and I’d like for the center point of the scaling action to be where the mouse is located on the object.

I tested this in the editor with fixed values, by moving the PivotOffset to the right of my 2d node, the scale attribute is correctly taking that point as scaling pivot.
Then I switched to doing the same thing in GDScript, I would:

		if event.button_index == BUTTON_WHEEL_UP:
			var pos = get_global_mouse_position()
			pivot_offset = pos
			scale += Vector2(0.1,0.1)

(and similar for the wheel down)

The whole thing seems to work, the object gets scaled, if I print the value of pivot_offset I can see it getting set to the mouse coordinates, but regardless, the scaling ignores the pivot’s new position and will always scale it up or down from the top-left corner.

What am I doing wrong?

there’s no pivot_offset property to change for Node2D nodes and its inherited nodes, so this is a Control node?

if it’s a Control node/its inherited, then it’s easily scale the Control Parent Node of this child. the child who you put in center of this control node will scale accordingly at center

yep, sorry when I wrote “2d node” I didn’t mean a Node2D one specifically!

It’s definitely a Control node.
This is parented under a Node2D that is the root of that tscn.

If I understand your message correctly, you’re suggesting I could make the Control node itself the root of the scene and go with that?


GIF:
scrollscale
The Control looks like in 2d scene:

the TextureRect:

the script code:

extends Node2D

@onready var control_node:Control=$Control

func _input(event):
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_WHEEL_UP:
		var pos = get_global_mouse_position()
		control_node.pivot_offset = pos
		control_node.scale += Vector2(0.1,0.1)
	elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
		var pos = get_global_mouse_position()
		control_node.pivot_offset = pos
		control_node.scale -= Vector2(0.1,0.1)
1 Like

Thanks for taking the time to put together that example.

I tested it first on a new empty scene, just creating a new Control and TextureRect.
Everything works as expected.

Then I tried to apply the same logic in my current scene, and the pivot_offset changes are ignored again.

My scene structure looks like yours, just with a couple of Node2Ds under the Control itself.

At game start I’m instantiating and parenting other scenes under those container nodes, which in return can contain other control nodes:
image

At fist I thought this was the issue, but even when I don’t add any child, I still don’t get the expected behaviour.

change the StoryUnitsContainer to Control type node

Ok, after a bit of more testing I know what’s happening.
My Control node is wider than the game screen:

So when the mouse pointer is -say- at [300,300] in global coordinates, that position corresponds to an out-of-screen position on the Control node.

I have to offset the mouse position by the top-left coordinate of the Control node and then the position of the cursor will match the underlying area of the Control.

Oki… more weirdness :slight_smile:

The scaling works, but there’s something else I’m not getting.
Every time I scroll the wheel, before starting to scale, the Control suddenly jumps towards the cursor.

I thought this was a refresh issue with moving the pivot_offset inside an if statement, so I moved the offset outside of it:

func _input(event):
	var pos = get_global_mouse_position()
	control_node.pivot_offset = pos-position #convert global position to local relative to the Control

	if event is InputEventMouseButton:

		if event.button_index == MOUSE_BUTTON_WHEEL_UP:
			control_node.scale += Vector2(scale_factor,scale_factor)

		if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
			control_node.scale -= Vector2(scale_factor,scale_factor)

And something odd happens.

  • I scale the Control up or down, no problem
  • now the Control is set to some scale
  • I start moving the mouse around, without touching any button or wheel, just moving the pointer
  • as per above code, this will shift the pivot to a different position and will ALSO move the Control ??

I’d like to attach a video capture, but I’m not allowed as I’m a new user :confused:

Ok, can attach files now.

scaling

I scale the control, then I just move the mouse…

oki… I had to implement my own offset by hand, since the pivotOffset won’t be stable.
Here’s what I’m doing after making sure that the pivotOffset is at coordinate (0,0) of my Control node:

		if event.button_index == MOUSE_BUTTON_WHEEL_UP:
			# first scale the Control up
			control_node.scale += Vector2(scale_factor,scale_factor)

			# compute the location of the Control node's center after resizing
			var newCenter = (control_node.position + (Vector2(control_node.size)/2)) * scale_factor

			# compute the vector from the new center to the mouse position
			# this gives me the direction I should shift the whole Control node
			# in order to have its center going towards the cursor
			var direction_to_mouse = mouse_position - newCenter

			#offset the control node position proportionally to the scale factor 
			control_node.position += direction_to_mouse*scale_factor

For the scale-down case (MOUSE_BUTTON_WHEEL_DOWN) I would just reverse the vector to the mouse position.
This gives me the correct behaviour while keeping pivot_offest at zero