Requested display width and height is not always obeyed?

Hey,

I recently upgraded to an ultrawide monitor and now something has gone a bit weird with my game. I first noticed that my main menu is out of position:

And then that the battle map is no longer in the right place:

Both elements are programmatically centered using window.get_size().

I seem to have figured it out now. My game.project contains:

[display]
width = 1280
height = 800
high_dpi = 0
fullscreen = 0

When I check the result of window.get_size() with debugger, I see it returns 1600,1000. If I run with fullscreen=1 I get the expected resolution.

Is the display width and height only a suggestion when running in windowed mode?

I came across Window.get_size() not returning the actual size and tried adding defos with this in my game.project, which seems to fix it to the expected size for windowed and fullscreen:

[defos]
view_width = 1280
view_height = 800

Just trying to understand if this is an expected outcome or a bug? Did I miss a bit of documentation somewhere that explains what is happening?

This makes me think you have some kind of display scaling going on with a 125% scale. 1280*1.25 = 1600 and 800*1.25 = 1000. What does window.get_display_scale() return?

1 Like

I was going to say I don’t think so, based on looking at output from defos earlier, but indeed you are correct - it returns 1.25.

If I tweak the code for centering the map a bit like this, then the outcome is correct:

local window_width, window_height = window.get_size()
local window_scale = window.get_display_scale() -- returns 1.25
window_width = window_width / window_scale
window_height = window_height / window_scale

I’m super curious to go test after work if this also sorts out the issue I had a few months ago with a parallax background getting deleted too soon on web platform…

Do you think it would be worth it for me to go find a few places in documentation where it may make sense to warn people about this? Maybe here and in the APIDOC for get_size()?

Sure, improving the documentation is never a bad idea!

Alright, I will work on it, it probably won’t be quick, but I’ll start with the manual and then go figure out the API documentation later.

1 Like

Decided that before I go and spread more confusion by updating documentation, I should first try to make sure I completely understand what’s going on. I found more issues related to this in my Gunit testing library’s own test code - seems my confusion is two-fold:

  • window.get_size(): when should I divide by window.get_display_scale() and when not?
  • Mouse / touch input: when to use action.x and action.y versus action.screen_x and action.screen_y and depending on which, when do I use window.get_display_scale()?

To help clear it up I created a little example project (for now provided here as zip, but tell me if it is worth getting it into Git as an official example):
defold-display-scaling-example.zip (631.1 KB)

Unfortunately, I still haven’t figured out a clear answer that would let me say: “always do this and it will work correctly under every circumstance”. For now I’ll share the results of this morning’s testing here and come back to it again a bit later…

Windowed, 100% scale
Everything works as expected and none of this matters:

Fullscreen, 100% scale OR 125% scale
If I toggle fullscreen in game.project to on - everything works as expected and none of this matters:

Fullscreen (via defos), 100% scale
I realize defos is an extension, so not official, but interesting to note at least - if I toggle to fullscreen with defos.toggle_fullscreen() things get very broken - placing things with action.x is mostly correct, but everything else is way off:

Fullscreen (via defos), 125% scale
Here things go completely haywire, only action.y is perfect and action.x is close to right:

Windowed, 125% scale
Calculations based on window.get_size() need to be divided by window.get_display_scale():

The action.x and action.y are accurate and shouldn’t be adjusted:

But if you use action.screen_x and action.screen_y you do need to adjust by window.get_display_scale():

Thanks Matt. We’ll write an answer to your questions next week when we are back from Gamescom.

Thank you, no rush, I’ll be on vacation next week anyway…

It was great meeting you guys in person and thanks again for all the Defold swag! <3

Enjoy the rest of Gamescom!

PS. Not sure if it is still accurate, but I found this post from 2019, which seems to suggest that I should avoid using action.x and action.y…

1 Like

TL;DR:
action.x and action.y are the action positions in virtual coordinates, which are needed to detect clicks in the GUI using gui.pick_node(). Most likely, you don’t need them for any manual calculations.
Use action.screen_x and action.screen_y instead.


Full Explanation

The engine does the following steps to get these values:

  1. Raw Input: Defold receives mouse/touch coordinates in actual screen pixels (where the user clicked).
  2. Virtual Scaling: These pixel coordinates are scaled to match your game’s virtual resolution (set in game.project under display.width and display.height).
  3. Pixel Centering: A 0.5 pixel offset ensures coordinates point to the center of pixels rather than their corners.

What This Means for You

  • action.x and action.y are always in your game’s virtual coordinate space.
  • If your game is set to 960x640 virtual resolution, coordinates will be 0–960 horizontally and 0–640 vertically.
  • This works regardless of the actual screen size — a 1920x1080 screen or a 480x320 screen will both give you the same virtual coordinates.

What Should You Use Then?

It’s better to use action.screen_x and action.screen_y for everything you want to calculate yourself.

Screen space is a universal space that can be used to convert different coordinates between different systems. For example:

  • From world to GUI
  • From GUI to world (depends on camera setup)
  • From a GUI node with one scale setting to a GUI node with another

Helpful Functions for Screen-Space Conversions

We don’t have such functions for go because they highly depend on the camera/render_script setup. (We plan to add them under the camera.* or go.* namespaces.)

In the meantime, here are examples of helper functions you can use:

5 Likes