Gui.pick_node() always returning false (SOLVED)

Hey there,

I have a problem using gui.pick_node().
The function returned false when I clicked on my gui-node and I figured it has to do with my camera, render-script and / or gui-positioning/-scaling/-resizing (player always centered + zoom function, gui-positions, gui-scale and gui-size manually altered to maintain size and position when zoomed in).

To check, where exactly the “clickable-area” of the node starts, I made the following loop in on_input():

local crafting_node = gui.get_node("crafting_slot_bg_1")
local picked = false
for y=-3000, 3000 do
	for x=-3000, 3000 do
		local p = gui.pick_node(crafting_node, x, y)
		if p then
			print("picked: "..x.." : "..y)
			picked = true
			break
		end
	end
end
pprint(picked)

When called, the loop takes a while to finish and then prints out… “false” !

Do I have to set up the Node to react to clicks or am I doing something generally wrong?

~Daniel

What is the position of the node? Could you try:

print(gui.get_position("crafting_slot_bg_1"))

What kind of modifications have you made to your render script? Specifically how do you render your gui? Is it not rendered in screen space?

The output of the print is:
vmath.vector3(-355.58197021484, 40.946918487549, 1)

This is how I’ve set up the gui:


I moved it, so the bottom left of the default “frame” is the center of the gui, because of the changed camera “frame”.

How can I see how the gui is rendered?
These are the changes I made to the render script:

-- -------------- changes in function init() -------------- --
    self.zoom_factor = 1
    self.visible_frame = {x1=0, x2=0, y1=0, y2=0}
    self.zoom_percent_w = 0
    self.zoom_percent_h = 0


-- -------------- changes in function update() -------------- --
	local w = render.get_width()
	local h = render.get_height()
	local zoom_w = w / 201 * self.zoom_factor
	local zoom_h = h / 201 * self.zoom_factor
	self.zoom_percent_w = ((w / 2 - zoom_w) - (-w / 2 + zoom_w)) / (w / 100)
	self.zoom_percent_h = ((h / 2 - zoom_h) - (-h / 2 + zoom_h)) / (h / 100)
	
	self.visible_frame = {
		x1 = -w / 2 + zoom_w,
		x2 = w / 2 - zoom_w,
		y1 = -h / 2 + zoom_h,
		y2 = h / 2 - zoom_h
	}
	
	render.set_projection(vmath.matrix4_orthographic(-w / 2 + zoom_w, w / 2 - zoom_w, -h / 2 + zoom_h, h / 2 - zoom_h, -1, 1))


-- -------------- function on_message() -------------- --
    if message_id == hash("clear_color") then
        self.clear_color = message.color
        
    elseif message_id == hash("set_view_projection") then
        self.view = message.view
        
    elseif message_id == hash("get_window_dimensions") then
    	msg.post(sender, "get_window_dimensions_reply", {width = render.get_width(), height = render.get_height(),
    													 visible_frame = self.visible_frame,
    													 zoom_factor = self.zoom_factor, zoom_percent_w = self.zoom_percent_w, zoom_percent_h = self.zoom_percent_h})
    	
    elseif message_id == hash("set_zoom_factor") then
    	if message.zoom < 1 then message.zoom = 1
    	elseif message.zoom > 100 then message.zoom = 100 end
    	self.zoom_factor = message.zoom
    end

On top of that I commented all of the other render.set_projection() calls in the update() function of the renderscript so they don’t get called.

In on_message() of the gui, this is what I do on “get_window_dimensions_reply”:

if message_id == hash("get_window_dimensions_reply") then
    	self.window_dimensions = message
    	self.window_dimensions_set = true
    	msg.post(sender, "get_window_dimensions")
    	
    	local visible_width = self.window_dimensions.visible_frame.x2 - self.window_dimensions.visible_frame.x1
    	local visible_height = self.window_dimensions.visible_frame.y2 - self.window_dimensions.visible_frame.y1
    	
    	for node_id, prop in pairs(ui_elements) do
    		local node = gui.get_node(node_id)
    		local pos = gui.get_position(node)
    		local size = gui.get_scale(node)
    		
    		-- initial position
    		if prop.init_pos.x == nil then
    			prop.init_pos.x = pos.x
    			prop.init_pos.y = pos.y
    			prop.init_pos.z = pos.z
    		end
    		
    		-- initial size
    		if prop.init_size.x == nil then
    			prop.init_size.x = size.x
    			prop.init_size.y = size.y
    			prop.init_size.z = size.z
    		end
    		
    		pos.x = (prop.init_pos.x / 100) * self.window_dimensions.zoom_percent_w
    		pos.y = (prop.init_pos.y / 100) * self.window_dimensions.zoom_percent_h
    		gui.set_position(node, vmath.vector3(pos.x, pos.y, pos.z))
    		
    		size.x = self.window_dimensions.zoom_percent_w / 100
	    	size.y = self.window_dimensions.zoom_percent_h / 100
    		gui.set_scale(node, vmath.vector3(size.x, size.y, size.z))
    	end
end

Ok, so if the position of the crafting_slot_bg1 is like above then gui.pick_node(gui.get_node("crafting_slot_bg1"), -355, 40) should return true (depending a bit on how you have anchored the node). The below snippet should be able to pick the node and return true:

local node = gui.get_node("crafting_slot_bg1")
local pos = gui.get_position(node)
print(gui.pick_node(node, pos.x, pos.y))

But it’s a different story if you’ve modified the render script to draw the GUI in something other than screen space and you want to pick the node based on on_input() action.x and action.y. In such a case you need to apply some translation from the mouse/touch coordinates to actual coordinates where stuff is rendered in your custom script.

The most common practice is to always draw the GUI in screen space and not mess around with the projection at all. This way gui.pick_node() and on_input() action.x and action.y will always match up.

That’s really strange…
I had a coordinate-translation implemented (because I saw someone mention it in another thread) but it didn’t work.
So I tried pick_node() with -355, 40 earlier, too and it returned false.

Now I tried again (just to make sure) and it suddenly returned true…
Because that surprised me, I tried the loop again and now it prints "picked: " with all the coordinates where pick_node() returned true.

I haven’t change anything other than adding the code you mentioned.
The more I work with Defold, the more I think it has more bugs than features :smile: :sweat_smile:
(Yes, I know that’s not fair because it’s possibly just me beeing stupid :joy:)

I tried to reproduce the “bug” but couldn’t.
Anyway, now everything seems to work fine. I hope, it keeps working :smiley:

Thank you for your help!

That’s always very annoying. Remember to also pay attention to the console and any Lua errors you might have (like indexing a nil value etc). If the current script scope contains an error the code will not execute beyond the point of the error.