You can do it with this code globally, no need for viewports (it actually uses the main viewport, which is the root node in Godot and always exists). This allows you to switch to any resolution you want (for it to work, the game as to be running at native res, for example with stretch mode 2D and aspect keep height).
extends Node2D
const RES_SD = Vector2(854, 480)
const RES_HD = Vector2(1280, 720)
const RES_FHD = Vector2(1920, 1080)
const RES_QHD = Vector2(2560, 1440)
const RES_UHD = Vector2(3840, 2160)
enum RESOLUTION { SD, HD, FHD, QHD, UHD }
export (RESOLUTION) var resolution := RESOLUTION.SD setget update_resolution
var viewport_size
func _ready():
get_viewport().connect("size_changed", self, "on_window_resize")
set_viewport_size()
on_window_resize()
func set_viewport_size():
var view_res = RES_SD
match resolution:
RESOLUTION.SD:
view_res = RES_SD
RESOLUTION.HD:
view_res = RES_HD
RESOLUTION.FHD:
view_res = RES_FHD
RESOLUTION.QHD:
view_res = RES_QHD
RESOLUTION.UHD:
view_res = RES_UHD
viewport_size = view_res
func update_resolution(res):
resolution = res
set_viewport_size()
on_window_resize()
func on_window_resize():
var viewport = get_viewport()
if viewport:
viewport.size = viewport_size
Just place that on a Node2D anywhere in your main scene and you can adjust the resolution with the inspector. It works well for going lower than native (for example if you want to increase performance in a 3D game). It does work in 2D but it can introduce shimmering on textures when the resolution is high (for example 4K res on a 1080p screen). If you enable FXAA in the project settings it helps a bit but does not eliminate the shimmering.
Another option is to use higher quality images. The sprite size and the image size do not have to be the same. For example, if the sprite is 64 x 128, make the image 128 x 256 and just scale the sprite down to 50%. This will be the same size, but with the advantage of additional filtering, which results in a higher quality image (and this is necessary with my script if you want to go to 4K).