Godot 4 - How do I make a SubViewport that supports both scaling GUI and multiple resolutions?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By thearst3rd

Hi all,

I’m trying to make a split screen 3D game using SubViewports. I have things mostly working but I have a few issues. So far, I’ve been making my game with the canvas_items stretch mode, so the GUI scales up as I make the window bigger/fullscreen. But in this mode, SubViewports in a SubViewportContainer don’t change in resolution, and so they look pixilated/low res as the game window gets bigger.

If I change the scaling mode to disabled, then the viewports render at full resolution and look crisp at all window sizes. But of course the UI doesn’t scale up, so they look relatively small as the window gets bigger.

Is there a way I can get the best of both worlds, where the UI scale changes based on the window size, and SubViewports also render at arbitrary resolutions based on the window size? I’ve looked around the documentation and online examples to see if I can make it behave like this, but I haven’t found anything yet.

Thanks!

:bust_in_silhouette: Reply From: Calinou

Is there a way I can get the best of both worlds, where the UI scale changes based on the window size, and SubViewports also render at arbitrary resolutions based on the window size?

Yes – check the 3D in 2D demo for an example of this.

You need a script that resizes the viewport when the root viewport’s size changes. Copying the script from the demo project for future reference!: (with parts not relevant to this question stripped):

var viewport_initial_size = Vector2()

onready var viewport = $Viewport
onready var viewport_sprite = $ViewportSprite

func _ready():
    #warning-ignore:return_value_discarded
    get_viewport().connect("size_changed", self, "_root_viewport_size_changed")
    viewport_initial_size = viewport.size


# Called when the root's viewport size changes (i.e. when the window is resized).
# This is done to handle multiple resolutions without losing quality.
func _root_viewport_size_changed():
    # The viewport is resized depending on the window height.
    # To compensate for the larger resolution, the viewport sprite is scaled down.
    viewport.size = Vector2.ONE * get_viewport().size.y
    viewport_sprite.scale = Vector2(1, -1) * viewport_initial_size.y / get_viewport().size.y

The above script and demo project are for Godot 3.x, but the same approach can be used in Godot 4.0.

Thank you, this really helped!

Instead of using a Sprite, I used a TextureRect since it was a control node that I could easily put into an hbox. But it works very well, thanks a ton!

thearst3rd | 2022-08-28 19:29