+3 votes

So I'm trying to do pixel perfect scaling in definite increments (1x 2x 3x etc) in fullscreen.

I initially was handling this through the camera2d zoom function, but that doesn't work as the pixel grid is not preserved when zooming

this post talks about what I'm trying to do: https://github.com/godotengine/godot/issues/6506

but the code presented there is obsolete as it's based on godot 2.1 function

The final message mentions that this is still doable in 3.0, through the viewport api, but doesn't really explain how.

Can someone help me figure out how to do this kind of scaling in godot 3.0

asked Mar 12, 2018 in Engine by Ellie93 (23 points)

2 Answers

0 votes

A method that can be used to easily control the viewport pixel size is Viewport's setsizeoverride method. You can use it to control the root viewport size without needing a special scene hierarchy.

For example, to make sure that the viewport doesn't scale anything, setsizeoverride to the same as OS.getwindowsize()

# maybe in an autoload/singleton but in a scene script should also work

func _ready():
    get_viewport().connect("size_changed", self, "window_resize")

func window_resize():
    get_viewport().set_size_override(true, OS.get_window_size())

Use this with Stretch mode 2d and Aspect ignore.

The size you send to setsizeoverride controls how many pixels the viewport takes from the source world_2d. In this case we set the number of pixels width/height that the viewport takes to be the same as the size of the window. Result is that no scaling/stretching occurs.

You will need to adjust the viewport's globalcanvastransform so it is centered correctly on your content.

To do x2, x3 scaling if the window size is large enough, then just check the window size from getwindowsize(). If it is large enough modify the size sent to setsizeoverride. (Eg. if it is >= 2x the size, then you can give setsizeoverride half of the window size.)

For your scenes, make sure your actual content is in the middle, and then surround it with whatever you want to show on the edges.

answered Mar 14, 2018 by chanon (193 points)
edited Mar 14, 2018 by chanon

I tried the code you posted on github, it does scale the content by integer but it still isn't pixel perfect, as you can see in the image of my game scaled x2 the rotated bullets have visible pixels smaller than ships, and the lines I drew with draw_line() use smaller pixels than the sprites. Is there any way to use the scaling by integer while also preserving the original pixel size just like the viewport stretch setting?
enter image description here

+2 votes

Unfortunately chanon's script was made for 2d stretch mode which is not good for pixel perfect games. I adapted the scripts to use viewport stretch mode in Godot 3. Set the following script as an Autoload:

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))
answered Apr 6, 2018 by sysharm (22 points)

Thank you for this. This post and script was just what i was looking for!

Pixel perfect makes my camera abit jaggy when moving diagonally, but thats another story.. Will have to make some clever camera script now.

i'm new so not sure this would work but in game maker i would usually draw things (including camera locations) at rounded coordinates.
usually rounded to the nearest integer, but perhaps in godot u need to round by ur upscale factor?
note i wouldn't actually change the location of the objects, just the location they draw at

Trying this with Godot 3.1 beta 2 and the script will never be called. Only when setting the scale mode to 2D. But not when using "VIEWPORT". Breaking change in 3.1?

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.