Click not being correctly translated to world coordinates

Hi,

Ive wasted 2 days on this and still can’t figure it out. I have a game object in my world that is not being detected on click, but when I click somewhere to the left its being detceted.

I have ensured that I am converting to world coordinates and that my sprite and collision components are at 0, but still no luck.

I am using defold orthographic as my camera systsem: https://github.com/britzl/defold-orthographic

Here is my game object that I want to click on:

The tower has a script with that reports its position as:

print("Tower position" .. go.get_position())

This returns the value:

DEBUG:SCRIPT: Tower positionvmath.vector3(-38, 168, 0)

Below is my code that is using a raycast and collision component to detect the tower:

function get_tower_at_position(self, screen_x, screen_y)
    -- Convert screen coordinates to world coordinates
    local world_pos = camera.window_to_world(hash("/camera"), vmath.vector3(screen_x, screen_y, 0))
    
    -- Debug prints
    print("Screen position:", screen_x, screen_y)
    print("World position:", world_pos.x, world_pos.y)
    
    -- Create ray for physics check
    local from = vmath.vector3(world_pos.x, world_pos.y, 10)
    local direction = vmath.vector3(0, 0, -20)
    
    -- Debug visualization in world space
    msg.post("@render:", "draw_line", { 
        start_point = vmath.vector3(world_pos.x - 20, world_pos.y, 0),
        end_point = vmath.vector3(world_pos.x + 20, world_pos.y, 0),
        color = vmath.vector4(0, 1, 0, 1)
    })
    msg.post("@render:", "draw_line", { 
        start_point = vmath.vector3(world_pos.x, world_pos.y - 20, 0),
        end_point = vmath.vector3(world_pos.x, world_pos.y + 20, 0),
        color = vmath.vector4(0, 1, 0, 1)
    })
    
    local groups = {hash("tower_collision")}
    local result = physics.raycast(from, direction, groups)
    
    if result then
        print("Hit tower at world position:", world_pos.x, world_pos.y)
        return result.id
    else
        print("No tower hit at world position:", world_pos.x, world_pos.y)
        return nil
    end
end

I am using screen_x and screen_y as the input coordinates:

Below line is how I am calling the function from on_input

local tower = get_tower_at_position(self, action.screen_x, action.screen_y)

But when I click exactly on the tower, I get the below debugs:

DEBUG:SCRIPT: Screen position:	672	1093
DEBUG:SCRIPT: World position:	-39.999996185303	167.91665649414
DEBUG:SCRIPT: No tower hit at world position:	-39.999996185303	167.91665649414

But when I click somewhere to the left of the actual tower position, I get the following:

DEBUG:SCRIPT: Screen position:	523	1066
DEBUG:SCRIPT: World position:	-164.16665649414	145.41667175293
DEBUG:SCRIPT: Hit tower at world position:	-164.16665649414	145.41667175293

Which is not the actual world position of the tower. This is really frustrating and I dont know what else to try.

Here is my outline of the components:

Please send help!

Have you checked with physics debugging that your collision shape is where you expect it to be?

Also, does it make any difference if you use screen_to_world() and action.x and action.y instead?

Yes, Ive turned on physics debugging and the colliders are drawn exactly where I expect them to be. And Ive tried with screen_to_world and action.x and action.y as well but that is worse off for some reason, the towers are detected much further away when I click elsewehere

Can you please create a ticket on GitHub and also share a sample project?

Minimal reproducible project attached

Ah, I see now. There are two problems:

  1. You are raycasting like this physics.raycast(from, direction, groups) when it should be physics.raycast(from, to, groups).
  2. A raycast will not trigger if it is done from within a collision shape.

You will solve 1) by raycasting a short distance where to is based on from.

Solving 2) is a bit more problematic, but I think I would try to cast 4 rays in the cardinal directions from the edges of the screen to the world position, and raycast with the option all set to true, and then grab the hit closest to the target position as the clicked object. Like this:

	local to = vmath.vector3(world_pos.x, world_pos.y, 0)
	local groups = {hash("tower_collision")}
	local options = {all=true}
	local left = physics.raycast(to - vmath.vector3(500, 0, 0), to, groups, options)
	local right = physics.raycast(to + vmath.vector3(500, 0, 0), to, groups, options)
	local down = physics.raycast(to - vmath.vector3(0, 500, 0), to, groups, options)
	local up = physics.raycast(to + vmath.vector3(0, 500, 0), to, groups, options)
	if left and right and down and up then
		local id = left[#left].id
		print("Hit tower", id, " at world position:", world_pos.x, world_pos.y)
		return id
	else
		print("No tower hit at world position:", world_pos.x, world_pos.y)
		return nil
	end
1 Like