2D scaling with black borders

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


For a pixel perfect game, I need to scale up my content since very low resolution are not very suitable for today’s screens. However, if I set the aspect to Keep, my pixel art is scaled up in a wrong way : it must be only multiples of the original resolution, with black borders on the sides.

I found in the documentation how to set the viewport to keep a given resolution, with get_viewport().set_size_override(true, Vector2(320, 240)), but I didn’t found how to set how the viewport is rendered in the window, since it’s currently scaled up awfully.

Previously, I used set_render_target_to_screen_rect to set it. However, with Godot 3, it seems the function was removed and I didn’t manage to found an alternative.

How can I make sure my game is keeping the original resolution or a multiple of it, with black borders if the window is bigger than the displayed game?

:bust_in_silhouette: Reply From: Banbury

I cribbed this from Reddit:

extends Node

onready var root = get_tree().get_root()
onready var base_size = root.get_rect().size

func _ready():
    get_tree().connect("screen_resized", self, "_on_screen_resized")

    root.set_size_override(false, Vector2())

func _on_screen_resized():
    var new_window_size = OS.get_window_size()
    OS.set_window_size(Vector2(max(base_size.x, new_window_size.x), max(base_size.y, new_window_size.y)))

    var scale_w = max(int(new_window_size.x / base_size.x), 1)
    var scale_h = max(int(new_window_size.y / base_size.y), 1)
    var scale = min(scale_w, scale_h)

    var diff = new_window_size - (base_size * scale)
    var diffhalf = (diff * 0.5).floor()

    root.set_rect(Rect2(Vector2(), base_size))
    root.set_render_target_to_screen_rect(Rect2(diffhalf, base_size * scale))

Save this script in a .gd file and go to Scene → Project Settings →
Autoload, and add the script. This will make the script run
automatically when the game starts.

Thank you for your answer, however this script (I used something similar previously) is not compatible with Godot 3, since set_render_target_to_screen_rect was removed.

Löne | 2018-03-18 09:39

A lot of stuff got lost between Godot 2 and 3. It’s not easy to keep track.

Maybe a feature request on Github is in order?

Banbury | 2018-03-18 09:48

Yes, I’ll open a feature request. Still looking for a possible hack, however.

Löne | 2018-03-18 11:18

:bust_in_silhouette: Reply From: sysharm

Reading the scripts I found around here about this I think I was able to adapt them to Godot 3. The function you were looking for is set_attach_to_screen_rect. Set the following script as an Autoload script like the other ones and don’t forget to set stretch mode as ‘viewport’ and aspect ‘ignore’:

extends Node

# don't forget to use stretch mode 'viewport' and aspect 'ignore'
onready var viewport = get_viewport()

func _ready():
	get_tree().connect("screen_resized", self, "_screen_resized")

func _screen_resized():
	var window_size = OS.get_window_size()
	# see how big the window is compared to the viewport size
	# floor it so we only get round numbers (0, 1, 2, 3 ...)
	var scale_x = floor(window_size.x / viewport.size.x)
	var scale_y = floor(window_size.y / viewport.size.y)
	# use the smaller scale with 1x minimum scale
	var scale = max(1, min(scale_x, scale_y))
	# find the coordinate we will use to center the viewport inside the window
	var diff = window_size - (viewport.size * scale)
	var diffhalf = (diff * 0.5).floor()

	# attach the viewport to the rect we calculated
	viewport.set_attach_to_screen_rect(Rect2(diffhalf, viewport.size * scale))

Here’s a work-around that puts black bars up instead of the flickering splash screen: Pixel-Perfect scaling script, ported to Godot 3. · GitHub

Dan Helfman | 2019-01-28 00:47