Screen To World example doesn't work correctly

So I’m struggling with Screen to World again. Well actually my son is and since I solved it before in my game I thought I could help him. But man it’s not as easy as I hoped. I know I used orthographic camera before but we really want to stick with the updated defold built in camera functions.

So we look at the example:
Screen to World

And it works when you run it; however, if you resize the window this is where the issues start. The larger the window change the more offset the OS mouse is from the in-game cursor.

So my question is, how do we properly calculate and translate the OS cursor position to the in-game cursor position after resizing the window? I did try sending a message from the default render script and have that working but it still wasn’t working properly in my screen to world function.

This is pretty basic stuff, I was hoping defold had a sample out there that would make this so much easier. If one exists, please let me know where. The one example they have has the same issue we have :-(.

1 Like

Seems to work OK for me, on Linux. A Microsoft thing?

Did you try the example in the link? Did you download it and run it locally? Not resize the web browser window, but actually run it locally?

I just did a git pull and now it is broken as you say! This commit broke it:

commit 8d609fa77dfdbf45f8487c54020fa8f0f1710dec (HEAD -> master, tag: v8d609fa77dfdbf45f8487c54020fa8f0f1710dec, origin/master, origin/HEAD)
Author: Björn Ritzl <bjorn.ritzl@gmail.com>
Date:   Tue Jul 1 08:48:29 2025 +0200

The change to bee.script at fault is:

This worked:
...
local DISPLAY_WIDTH = sys.get_config_int("display.width")
local DISPLAY_HEIGHT = sys.get_config_int("display.height")
...
       w = w / (w / DISPLAY_WIDTH)
       h = h / (h / DISPLAY_HEIGHT)
...

This does not

...
 local scale = window.get_display_scale()
 w = w / scale
 h = h / scale
 ...
1 Like

Thank you!!! I’ll try this out.

Edit: I tried it and yes that fixes it.

From my view, the bee.script should be like that, i.e.

  1. We need to use screen_x and screen_y instead of x and y.
  2. We don’t need to get the display scale, because screen_x/y is in the same unit as the values from window.get_size().


-- function to convert screen (mouse/touch) coordinates to
-- world coordinates given a camera component
-- this function will use the camera view and projection to
-- translate the screen coordinates into world coordinates
local function screen_to_world(x, y, z, camera_id)
	local projection = camera.get_projection(camera_id)
	local view = camera.get_view(camera_id)
	local w, h = window.get_size()

	-- https://defold.com/manuals/camera/#converting-mouse-to-world-coordinates
	local inv = vmath.inv(projection * view)
	x = (2 * x / w) - 1
	y = (2 * y / h) - 1
	z = (2 * z) - 1
	local x1 = x * inv.m00 + y * inv.m01 + z * inv.m02 + inv.m03
	local y1 = x * inv.m10 + y * inv.m11 + z * inv.m12 + inv.m13
	local z1 = x * inv.m20 + y * inv.m21 + z * inv.m22 + inv.m23
	return x1, y1, z1
end

function init(self)
	-- send input events to this script
	msg.post(".", "acquire_input_focus")
end

function on_input(self, action_id, action)
	if action_id == hash("touch") and action.pressed then
		-- convert mouse/touch screen position to world position
		local worldx, worldy = screen_to_world(action.screen_x, action.screen_y, 0, "#camera")
		local world = vmath.vector3(worldx, worldy, 0)
		go.animate(".", "position", go.PLAYBACK_ONCE_FORWARD, world, go.EASING_LINEAR, 0.5, 0)
	end
end

iotoHum’s solution fixes it for me.

The only place where we need display scale is for zooming in the camera. For example, here’s how to get a fixed fit projection with display scale taken into account using the camera component:

local DISPLAY_WIDTH = sys.get_config_int("display.width")
local DISPLAY_HEIGHT = sys.get_config_int("display.height")

--- Adjust camera zoom so that the original area covered by
-- display width and height of game.project is always visible
local function fixed_fit_projection(global_zoom, camera_url)
    local w, h = window.get_size()
    local scale = window.get_display_scale()
    local zoom = global_zoom * math.min(w / scale / DISPLAY_WIDTH, h / scale / DISPLAY_HEIGHT)
    go.set(camera_url, "orthographic_zoom", zoom)
end

--- Usage example:
function update(self, dt)
    fixed_fit_projection(0.6, "#camera")
end
4 Likes

Yes, this works, :grinning_face: and less code that looks much nicer.

1 Like

Great stuff! Thanks for pointing out the mistake! Does someone want to contribute a PR?

1 Like

And @aglitchman had already done so :heart: :folded_hands:

5 Likes

The web page needs to be updated as well (maybe that will happen later and is already planned, but wanted to make sure this doesn’t get missed). Thank you.