Is there an easy way to set and get position of a gui node when the screen have been resized? (SOLVED)

GUI coordinate spaces makes me confused.

Maybe I am doing something wrong but it feels like I am forced to do a lot of extra stuff to be able to set somethings position.

I have this scene

The blue dot is a child to the green dot which is a child to the red dot. I set the position to the dot with gui.get_screen_position() which works if the Project dimension == Screen dimension.

I have 5 different tests here.
White: Mouse position, this is the raw mouse position that I get with action.x
Red: This is the positions of the white node reported with gui.get_screen_position(), that we set to a parent of the actual red node that have Adjust Mode: Stretch.
Yellow: This is the position of the white node with to_real_screen_positions(gui.get_screen_position()), that we set to a parent of the actual red node that have Adjust Mode: Stretch.
Green: This is the positions of the white node reported with gui.get_screen_position(), we set this to the node directly without any Adjust Mode: Stretch.
Yellow: This is the position of the white node with to_real_screen_positions(gui.get_screen_position()), we set this to the node directly without any Adjust Mode: Stretch.

If we then change the size of the screen to be different than the project defined to something like
51

What happens if we then set the position of our things to different nodes?


That happens, we notice that the only way to get an accurate position is to

  1. Set the position on a node that have Adjust Mode: Stretch, but you probably have a sprite on it so you can’t have it on the node directly but need to do it on a parent (else the sprite would of course stretch with the window).
  2. Get the position of the node with gui.get_screen_position()
  3. Convert the position to real screen position with to_real_screen_position()

This seems quite convoluted as it requires so much more than just setting the position. The position we get from Defold isn’t the screen position, we need a parent for every single thing we want to move, we can only use Adjust Mode: Stretch on the nodes.

Is all this really needed or Is there an easy way to set and get position of a gui node when the screen have been resized?


function M.to_real_screen_position(pos)
  local screen = vmath.vector3(pos)
  screen.x = screen.x * (project_width/window_width)
  screen.y = screen.y * (project_height/window_height)
  return screen
end
2 Likes

You can set the position of nodes with any Adjust Mode if you have the right conversion from screen coordinates for each mode. I recently went through this with Tomires, maybe what I wrote here can help you. (the second half of the post). I think I have my formulas backwards compared to yours—dividing and subtracting instead of multiplying and adding—but it should still give you the right idea. With the “Fit” and “Zoom” Adjust Modes, nodes maintain proportion, so you need a uniform scale factor. Then you’ll also have an offset in x or y based on the difference in proportion from your project width/height.

4 Likes

Thanks @ross.grams as always Rendercam does it for you! :smiley:

I have to say though that isn’t this counterintuitive? When you say to the engine “place my node here” as a user I would expect it to be at that location regardless of which Adjust Mode the node I am moving is using. Shouldn’t the engine take care of transforming the translation depending on the Adjust Mode? Surely the most common user case would be to “place my node here” and not “place my node where it should be with the adjust mode applied”? Interested what people think about this.

2 Likes

Yeah, it’s definitely not intuitive. I can’t imagine someone not being confused, honestly. The difference between actual screen coordinates and the “stretchy” coordinates of action.x and action.y is pretty weird too, though I guess that makes some sense for mobile devices with really high-res displays?

Actually, now that I think about it, I am sure this is all designed so that nodes don’t change their positions no matter what Adjust Mode they use or how the window is changed. That is, you can call gui.get_position on a node and always get the same result.

Maybe we just need gui.set _screen_position?

1 Like

I’m trying to wrap my head around this, with the simplest example possible. The aim is to make the “pie” node always appear in the top right corner.

local node = gui.get_node("pie")
local window_w,window_h = camera.get_window_size()
local config_w = sys.get_config("display.width")
local config_h = sys.get_config("display.height")
local x = window_w * (config_w/window_w)
local y = window_h * (config_h/window_h)
gui.set_position(node, vmath.vector3(x,y,0))

The result (before and after window resize):


What changes are needed for it to work?

GUIPositioning.zip (234.9 KB)

Edit: Found this post. Is this what I need to do? This is hard!

I decided not to reinvent the wheel and solved it with Rendercam:

local rendercam = require "rendercam.rendercam"

function init(self)
	rendercam.add_window_listener(msg.url())
end

function on_message(self, message_id, message, sender)
	if message_id == hash("window_update") then
		local x,y = rendercam.screen_to_gui( message.window.x, message.window.y, gui.ADJUST_FIT )
		gui.set_position(gui.get_node("pie"), vmath.vector3(x,y,0))
	end
end

:star_struck:

1 Like

What happens if you set the X and Y anchors?

Not sure what you mean?

All GUI nodes have “X Anchor” and “Y Anchor” properties. If you set these to “right” and “top” then it will move relative to the top right corner when the window is resized. You can set them in the editor, or set them at runtime with gui.set_xanchor and gui.set_yanchor. You shouldn’t have to set its position manually.

1 Like

Ah, I was thinking about that. My use-case, however, is a mini map; a circle of point of interest around the player. These are constantly moving (depending on the player’s position) so the anchors would change all the time. Maybe there is a way to do it, but I can’t figure it out! In any case, Rendercam works now.