I'm currently working on a project where viewports are very important, and I just realized there doesn't seem to be a proper way to make sure they stretch to the size of the container. Here's an example:

I have Pong here running fine.

When I try putting Pong in a SubViewport that's smaller than its original size, it gets cut off.

I tried scaling the Pong game itself, which works visually but messes up a lot of things:

get_child(0).scale = Vector2(get_parent().size.x / 1152, get_parent().size.y / 648) (this is in the subviewport)
The problem with this is that everything physics related changes depending on how big the viewport size is. For example, if the ball is moving 2 pixels to the right while the scale is 0.5, it's essentially going to move twice as fast.

I'm a bit out of luck on this. Does anyone know if I can stretch and scale a subviewport without changing any logic? Something like the canvas_items stretch mode, but only for that viewport. Any help is appreciated, thanks.

  • Oh, I think I've found a better answer again, it's just poorly named. SubViewport.size_2d_override. Set that to your target resolution, set size2D_override_stretch to true, and then just make sure you're keeping the right aspect ratio (or don't, up to you)

    I tried it out with this convoluted setup of containers:

    and I attached this to get the aspect ratio correct (but only at runtime)

    extends AspectRatioContainer
    
    # Called when the node enters the scene tree for the first time.
    func _ready() -> void:
    	var resolution := Vector2($SubViewportContainer/SubViewport.size_2d_override)
    	ratio = resolution.x / resolution.y

    Hopefully this works out better for you. I'm sorry if I've led you down the wrong rabbit holes. I've still so much to learn too.

You could try keeping the scale matching your default but rendering to a TextureRect and scaling that

Also gives you the option to control aspect ratio

Ah ok actually there is a much cleaner way of doing it, but only for Window, not SubViewport.

Content Scale Size set to your project resolution.
Mode set to Viewport, and Aspect set to Expand.

This way, the Window's content will be rendered at your target resolution but the result of that render will be scaled to the Viewport.

    award Windows work, thanks! It turns out that a Window inherits Viewport, so it has a subviewport built-in. I embedded the window and made it borderless which seems to work well enough.

    My main problem now is that the window doesn't respect its position in the tree. It's always above everything else, and doesn't follow the position/scaling/etc of its parent. I wrote some code to have it resize to fit its parent but it's such a pain having to work around having zero control over it. There is no modulate as well so I can't force it to be transparent like every other Control node.

    I guess these are problems for another post. Thanks anyways.

    Oh, I think I've found a better answer again, it's just poorly named. SubViewport.size_2d_override. Set that to your target resolution, set size2D_override_stretch to true, and then just make sure you're keeping the right aspect ratio (or don't, up to you)

    I tried it out with this convoluted setup of containers:

    and I attached this to get the aspect ratio correct (but only at runtime)

    extends AspectRatioContainer
    
    # Called when the node enters the scene tree for the first time.
    func _ready() -> void:
    	var resolution := Vector2($SubViewportContainer/SubViewport.size_2d_override)
    	ratio = resolution.x / resolution.y

    Hopefully this works out better for you. I'm sorry if I've led you down the wrong rabbit holes. I've still so much to learn too.

      award Now this fixed all my problems, thank you! This seems like the best way to do it.

      award I'm sorry if I've led you down the wrong rabbit holes.

      No worries, I've really been trying everything anyways and very few people seem to have delved this deep into viewports. "Size 2D Override" seems like a misleading name, I've always thought it was something similar to the minimum size of the viewport. Turns out it sets the resolution of the viewport. No doubt this thread will help people in the future.