Trouble with go.world_to_local_position – seems to be adding positions together?

I am trying to determine where on a game object the user has clicked, in terms of that game object’s local space. (For example, if the user clicks the game object dead on its local origin, I want the returned position to be (0, 0).) I’m using go.world_to_local_position, which I think is what I want, but it’s not returning the position I’d expect. Am I using the function incorrectly?

My actual project uses defold-input’s cursor together with defold-orthographic, but I’m still getting the behaviour in a minimal reproduction project that uses neither. I’m using Defold 1.10.0 on macOS, though as far as I can tell, go.world_to_local_position hasn’t changed since it was originally merged in last year.

These three green squares’ bottom left corners (marked in red) are at their local origins. Each has a collision shape that covers their sprites. One is placed at world origin, the second is at (250,250), and the third is at (550,0) with Z-rotation of 45.0°.

There is a simple cursor game object with a small kinematic collision object (and a white dot particle sprite) that follows the user’s mouse pointer. It is scripted thus:

function init(self)
	msg.post(".", "acquire_input_focus")
	self.touching = nil
end

function update(self, dt)
	self.touching = nil
end

function on_message(self, message_id, message, sender)
	if message_id == hash("collision_response") then
		self.touching = message.other_id
	end
end

function on_input(self, action_id, action)
	if action.x and action.y then
		go.set_position(vmath.vector3(action.x, action.y, 0))
	end

	if action_id == hash("touch") and action.pressed then
		print("Click action.x/y: ", action.x, action.y)
		local cursor_world_position = go.get_world_position(".")
		print("Cursor world position x/y: ", cursor_world_position.x, cursor_world_position.y)
		if self.touching then
			local object_world_position = go.get_world_position(self.touching)
			print("Clicked object: " .. self.touching .. " (object world position: ", object_world_position.x, object_world_position.y, ")")

			local click_local_position = go.world_to_local_position(cursor_world_position, self.touching)
			print("Object was clicked at local position: ", click_local_position.x, click_local_position.y)
		end
	end
end

When I run the game and click each square close to each one’s local origin:

DEBUG:SCRIPT: Click action.x/y: 	13.5	12.5
DEBUG:SCRIPT: Cursor world position x/y: 	13.5	12.5
DEBUG:SCRIPT: Clicked object: [/square_origin] (object world position: 	0	0	)
DEBUG:SCRIPT: Object was clicked at local position: 	13.5	12.5

DEBUG:SCRIPT: Click action.x/y: 	268.5	264.5
DEBUG:SCRIPT: Cursor world position x/y: 	268.5	264.5
DEBUG:SCRIPT: Clicked object: [/square_translated] (object world position: 	250	250	)
DEBUG:SCRIPT: Object was clicked at local position: 	518.5	514.5

DEBUG:SCRIPT: Click action.x/y: 	553.5	25.5
DEBUG:SCRIPT: Cursor world position x/y: 	553.5	25.5
DEBUG:SCRIPT: Clicked object: [/square_rotated] (object world position: 	550	0	)
DEBUG:SCRIPT: Object was clicked at local position: 	1103.5	25.5

I’ll be the first to admit that vector math’s not my strong suit, but a click at world (268.5, 264.5) atop a GO that has its own origin at world (250, 250) should mean the click’s at (18.5, 14.5) in that GO’s local space, not (250 + 268.5, 250 + 264.5) = (518.5, 514.5), right?

I feel like I must be missing something obvious. If anyone can set me right, I’d appreciate it.

This is how I’m getting local click coordinates in the meantime:

local object_world = go.get_world_transform(object_url)
local object_inverse = vmath.ortho_inv(object_world)
local cursor_world = go.get_world_transform("/cursor")
local cursor_from_object = object_inverse * cursor_world
local cursor_local_position = vmath.vector3(cursor_from_object.m03, cursor_from_object.m13, cursor_from_object.m23)

Is there any update to this?

I am running into the same issue, it seems the output of go.world_to_local_position is wrong, or at least in the wrong space. For example, the following script tries to compute the position of a game object in its own space:

local function matmul(m, v)
	local v4 = vmath.vector4(v.x, v.y, v.z, 1)
	local vt4 = m * v4
	return vmath.vector3(vt4.x, vt4.y, vt4.z) / vt4.w
end

function init(self)
	-- object starts at (1, 0, 0)
	local world_position = go.get_world_position(".")
	local local_position = go.world_to_local_position(world_position, ".")

	local local_position_mat = matmul(vmath.inv(go.get_world_transform(".")), world_position)

	pprint(local_position, local_position_mat) -- prints vmath.vector3(2, 0, 0), vmath.vector3(0, 0, 0)
end

I would expect local_position and local_position_mat to both be (0, 0, 0) (position of an object in its own space should be the origin), but for some reason go.world_to_local_position returns something different, and the documentation is not help.

I have found it’s always an issue on my end. But it’s not very intuitive on what is causing it. Even mutliple AIs screw this up. I wish it was more simple and easy to understand. I lot of times you’re missing the screen function. It’s easy to miss.

1 Like

To me it looks like a bug in the implementation of go.world_to_local_position. From the docs I gather this function should return the same position that go.set_parent would use(in case keep world transform = false).

We’re going to take a look at this! Something must have broken.

1 Like

Also gui.screen_to_local is not correct when the node is scaled.

gui.set_parent(node, parent, true) will change the scene position of the node when the scale of the parent is not 1.

This is a different issue. Please create a GitHub issue if there isn’t one already.