How do I make the playable area/rect follow the window size?

Godot Version

4.3

Question

I have a very simple setup:
image

The sprite2D is centered on the node2d’s rect (position 0,0) and if I hit play it looks like this:

Which is exactly what I want, however if I say rescale or fullscreen the window, the positioning is thrown off:

How can I work around this issue?

Note: I am aware of the display stretch settings:
image

Which re-scale the playable area according to the window size (and is very sorely lacking a global scaler/interpolator default setting) but that isn’t actually what I want, I want the playable area/rect to conform to the window anchored to the bottom center without scaling, I then intend to apply my own scaling logic whenever the window size changes (I intend to make use of the SVG importing features to make sure everything always looks good no matter how you scale the window)

If there is a way to set the viewport size the stretching mode uses to control it’s scale to always default to the current screen’s native resolution, that might work as a plan B (e.g. default scaling is what would be correct for whatever the current monitor’s fullscreen resolution is, that way there will never be any upscaling, only downscaling which is less of an eyesore)

But as far as I can tell, the default viewport size is a hardcoded value.

I am unsure what you want to do, but I think using a Camera2D Node and centering it on the player should solve your issue. Eventually you can control the zoom parameter of the camera if you want a particular scaling depending on viewport size.

1 Like

Edit: The camera solution seems to give me the behavior i want! Thanks!

I’m not using a camera at all, although I can try it I strongly doubt it would work the way I need :thinking:

The problem is that by default the playable area is set to the dimensions of the initial viewport size. If I change the window/viewport size, it is anchored to the upper left corner and keeps it’s original size no matter how the window gets resized.

The common solution to that problem is to use the display stretch settings
image

This makes it so that the playable area gets scaled to fit in the window no matter what it’s size is, and with that particular setup it keeps the original aspect ratio too.

The problem then is that it is ‘scaling’ the rendered image, so if the initial viewport size was smaller than the fullscreen resolution (which by default it is) then if you switch your game to fullscreen eveyrthing is upscaled and thus becomes a blurry pixelated mess.


And to solve that, what I want is for the playable area to resize (not scale, but change it’s dimensions) to always match the window’s size so that the playable area’s center is always the window’s center as well without any scaling applied (so if I make the window bigger, the icon stays in the center but isn’t scaled in any way, I want it to keep 1:1 scale)

And I would then compensate for the difference in scale by using my own scaling logic. I already have the beginnings of that scaling logic code.

What I do not have is a way to make the playable area dimensions always match the window dimensions without any sort of scaling applied to it.

I need a way to dynamically update the playable area size.

1 Like

Edit: Made a whole guide out of it:

To share an example of what I’m doing:

extends Sprite2D

var DevScreenHeight:float = 648.0 # Default viewport height
var Scale:float = 1 # Size Multiplier
var Window_Size:Vector2i

func _ready() -> void:
	Window_Size = get_viewport().size # Set window size variable to viewport size.
	scalesvg("res://icon.svg") # Import an image from SVG
	anchor_bottom_center() # Set initial position to the bottom of the screen.

func _process(delta: float) -> void:
	if Window_Size != get_viewport().size: # If window size has changed
		Window_Size = get_viewport().size # Update window size variable
		scalesvg("res://icon.svg") # Rescale the image
		anchor_bottom_center() # Set position to bottom again

func scalesvg(path_to_svg: String) -> void:
	var SVG: PackedByteArray = FileAccess.get_file_as_bytes(path_to_svg) # Load SVG to buffer
	var Bitmap: Image = Image.new()
	var DesiredScale: float = Window_Size.y / DevScreenHeight # Set scale according to window height relative to the default viewport height
	Bitmap.load_svg_from_buffer(SVG, DesiredScale * Scale) # Scale SVG and convert from buffer to bitmap
	texture = ImageTexture.create_from_image(Bitmap) # Apply the bitmap as a texture

func anchor_bottom_center() -> void:
	position = Vector2(0,get_viewport().size.y * 0.5 - texture.get_height() * 0.5) # Calculates the coordinate for the bottom center of the screen and offset it by the height of the bitmap

Result:

Default window size:

Fullscreen window size:

You can’t tell a difference from the thumbnails (but if you open them you’ll notice the fullscreen one is upscaled by quite a lot with no quality loss) because what i’ve done is taken advantage of the properties of SVG to scale the image losslessly, e.g. this sample game (if you can call it that) would be exactly the same quality visually on a 720p and 2160p display; similar to adobe flash (or just any vector graphics) games.

In the general purpose sense, this technique would be fantastic to use for UIs no matter if you’re making a 2D or 3D game.

But I’m hoping to take it further than that.

The biggest downside is that this isn’t particularly cheap. So when things need to be rescaled it may stutter quite heftily although it odesn’t in my case but that’s probably because I only have one, actually let me test it…

I tried with 50 sprites, it never stuttered while dragging and resizing the window but fps did dip down to 70 at worst (if i have just one it doesn’t make any sort of dent, for comparison).

Notably the bigger the resulting bitmap/texture is going to be after the scaling, the more expensive the process gets (and the cost increase is sorta exponential in that regard. For instance if I scale just one texture by 100 (that’s 12800x12800 resolution, obscenely huge) the process takes a few whole seconds, but if i scale it by 10 (1280x1280) it still only takes a matter of milliseconds, but the fps dips down to 50-60 and there are some microstutters (e.g. one sprite scaled by 10 is a little more expensive to scale than 50 sprites scaled by 1).

I think it’s a perfectly acceptable price though because we only need to do this sort of scaling whenever the sprites are first loaded and again when the window size changes (which could be accompanied by a loading screen).

Although it would be quite a lot more ideal if this scaling process could be offloaded to other threads than the game thread.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.