-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pixel-perfect scaling mode #6506
Comments
I actually made a script for this not too long ago, and all you have to do is set the script as an AutoLoad. It's simple enough, I don't really know if it needs to be built in.
|
Oh, looks great :) but is there a way to get rid of the black borders when the window is resized? Like, make the game scale according to the given resolution but expand beyond the borders so we get the chance to fill it with some nice texture for example, or simply have a little bigger field of view? |
Hey @Zylann, I came across this post (fortunately) while making my first dive into Godot Engine, coming from Unity. I'm just a hobbyist, with only a couple years in Unity, but I've been pretty impressed with Godot so far. Anyway, I'm posting because your post, and @CowThing's post made me think about possible other solutions. I tried out his script, and it does work nicely, but I desperately wanted to avoid black space if possible. Now, what I ended up doing, I haven't fully worked out yet, but I think its possible that it could be a solution, and I wanted your input before I just settle on it. What I did was set (in Display settings) my stretch mode to disabled. So by doing this, my game starts out at a pixel perfect scale, but resizing the window causes the viewable area to expand if the window is made larger, and the inverse if smaller, as you would expect. Then, instead of resizing the viewport and getting blackspace, I decided I would instead use the Camera2D node's zoom to handle the amount of space I show on either side. So in func _on_screen_resized() , I use the new window size to test the width in pixels against some const I made to hold my main target resolutions, which I got from a list reported by steam. Basically, if the window size is less than 1600, I zoom by 2x (0.5,0.5). If its between 1600 and 2200, I scale by 3x, if between 2200 and 3k, 4x. etc. These zoom amounts work nicely for my game which is 32 x 32 for a tile, or base size of an object. Basically, everyone wont see things EXACTLY the same, unless they are 1080, 1440, or 4k, but I plan to solve this by allowing a mouse pan to accomodate the remaining unseen portions. Anyway, I just wanted to toss that idea out for you if you dont mind showing more/less by a margin while always remaining pixel perfect. My game is top down so its not a deal breaker to show a little more or less between AR's or window sizes, especially if the player will be able to pan more/less depending on whats shown. Thanks for posting, big help to me! |
I think it would be nice for pixel art games so it probably should be part of engine. |
@tiltfox Prevents players from seeing regions they shouldn't while preventing black bars on any screen or orientation. Just a bit more work to get backgrounds set up that look nice.
Currently I don't need pixel perfection so I don't snap the zoom values. |
@tiltfox Using Camera zoom is nice and easy to do, but it won't scale the pixel grid, but the pixels themselves, even if zoom scales are integer. So you could end up with two moving objects overlapping a half of a pixel. This is not an issue for most games, but if you really want all game pixels to fit the viewport grid as in old-school games, the only way is to change the viewport size, not the camera zoom. But with this solution you get black borders, and I wonder if it's possible to get rid of them while still being able to get real blocky pixels. Maybe a different calculation? |
Ah I see, for the game I was making I wanted to keep it at the same resolution, just scale it up from there. I modified the code so that the resolution can change and it will still scale up based on the initial size. There are still small black borders on the sides, but they will never be more than one scaled up pixel large (so if the scale is 7, the black border would never be more than 6 pixels large, the code also centers the image, so it would be 3 pixels on each side). This is because as far as I can tell it's not possible to render only part of a pixel using this method, it needs to be whole pixels.
|
Just chiming in to say that I would love to see this as a built-in feature. For now though I am glad I found this discussion- lots of great insight on how to handle this via scripting. |
I love this idea @CowThing ! |
@HummusSamurai Please do not bump issues without contributing significant new information, use the 👍 reaction button on the issue's first post instead. |
the Viewport API in Godot has everything you need to make this yourself. As it's not a feature you might use commonly, I suggest just make a script and put it in the asset library for others to use. |
@reduz "the Viewport API in Godot has everything you need to make this yourself. As it's not a feature you might use commonly, I suggest just make a script and put it in the asset library for others to use." Any pointer as to how? The code written in this discussion is obsolete and based on 2.1 functions, and I don't think Viewport can be manipulated in that way anymore, especially in fullscreen. I've been trying to make something like that work for a week but nothing seems to really work, aside from zooming the camera (but that has the mentioned problem of not preserving the pixel grid) |
@Omiminpo Look in your Project Settings under "Display -> Window -> Stretch" Try setting "Mode" to "2D" and "Aspect" to "keep". You might want other settings, but you can play with them to get what you want. Does that help? |
This can scale to fractional values, which will look really bad when done with nearest-neighbor-filtered textures such as pixel art. This could be fixed by adding something like a "2D Integer" and "Viewport Integer" scaling modes, which would only resize or scale the 2D viewport by the largest integer factor possible (and fill the rest with black or another color). |
@Calinou it should not necessarily fill spaces with black if the view can expand further |
True, however, this would be only possible with the "2D Integer" scaling mode I proposed (and in 3D games only, based on my understanding), not the "Viewport Integer" scaling mode – unless you plan on implementing fractional pixel scaling, which is possible but doesn't look ideal. |
@Calinou You can set Aspect to "expand" to try to remove the black bars. |
@Calinou @Omiminpo I'm writing an article on how integer-only scaling can be done using an inner viewport so you can freely set what you want to display on the borders, but the gist of it is:
extends Node2D
const DESIRED_RESOLUTION = Vector2(320, 180)
func _ready():
self.get_viewport().connect("size_changed", self, "on_root_vp_size_change")
self.on_root_vp_size_change()
func on_root_vp_size_change():
var scales = self.get_viewport().size / self.DESIRED_RESOLUTION
var scaling_factor = floor(min(scales[0], scales[1]))
var actual_resolution = DESIRED_RESOLUTION * scaling_factor
$Control/ViewportContainer.margin_top = -actual_resolution[1] / 2
$Control/ViewportContainer.margin_bottom = actual_resolution[1] / 2
$Control/ViewportContainer.margin_left = -actual_resolution[0] / 2
$Control/ViewportContainer.margin_right = actual_resolution[0] / 2
var default_transform = Transform2D(Vector2(1, 0), Vector2(0, 1), Vector2())
$Control/ViewportContainer/Viewport.canvas_transform = default_transform.scaled(Vector2(scaling_factor, scaling_factor)) The hierarchy for this scene is: On the script I've purposely kept the inner viewport in the middle by setting all anchors to 0.5 and setting margins around that, but there are other ways to do it that are probably better, such as allowing viewport to be larger on x-axis so borders are only on the sides without breaking integer scaling. I´m not 100 % sure this is perfect, as I´m still playing with it, but seems to work well enough. |
Another method that can be used to easily control the viewport pixel size is Viewport's For example, to make sure that the viewport doesn't scale anything,
Use this with Stretch Mode 2d and Aspect ignore. The size you send to You will need to adjust the viewport's To do x2, x3 scaling if the window size is large enough, then just check the window size from 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. (You can have the top left of your actual content be at 0,0 as usual, just make sure your BTW I believe this is the Viewport API reduz had in mind when he commented. I just have to say that Godot is amazingly flexible and it seems the devs have thought of everything. |
Here is a full example
That's it. If it looks long its because of the comments. Here is a full example project: The example uses a 320x240 screen (content) size set in project settings with a 320x240 image with an alternating pixel background to check for artifacts. 2D pixel snapping is turned on and the image's filtering is off. Short version without comments:
Also, actually you can just use the 'Environment/Default Clear Color' setting in Project Settings if you just want a solid color that is not the default for the borders. In the example project I used a large ColorRect. UPDATE in case anyone sees this: sysharm over at the QA site has a better version that uses viewport stretch mode which is better suited for pixel games: |
n-pigeon
reduz
Why would this not be used commonly? There is definitely demand for it (in this thread, in the comments here and here and here) And I for one would want to see how my pixelart game looks without any halve-pixels scaled up pixels on screen. I agree with n-pigeon it probably should be part of the engine for everyone who created pixel art games. This is anything but a small portion of Godot users after all. |
I had an issue with my fonts becoming distorted if the resized window's size contained an odd number. I used this code in a
Hope that helps. This doesn't produce any black borders, bars, etc. I stumbled upon this issue from Google |
Note: This is now being discussed in godotengine/godot-proposals#1666. |
Credits goes to atomius0 & CowThing. Please refer to https://gist.github.com/atomius0/42b5605e9f4e5b171076cd0c4453d4bb and godotengine/godot#6506 for more infos.
There could be a new scaling mode in godot that preserves pixel matching on the screen.
For example, when the window resizes, instead of trying to fit the viewport on it in various ways, only do it with integer steps (x1, x2, x3...).
An example of this would be the GUI of Minecraft when set to Auto. If you resize the screen, it will change its scale but only by an integer factor, thus keeping game pixels and screen pixels in sync. So you can actually tell that the viewport scale is equal to the number of pixels a "game pixel" takes on screen.
I could investigate of a way to do this as a plugin, but I wonder if this can actually make it in the engine.
WDYT?
The text was updated successfully, but these errors were encountered: