I have come up with a solution, so for everyone looking for this kind of behavior, here is one way it can be done.
I would like to thanks bojidar-bg (bojidar-bg (Bojidar Marinov) · GitHub) for helping me out on IRC by directing me to a previous game that has similar behavior, GitHub - KOBUGE-Games/minilens: Cute puzzle platformer starring a cleaning robot on post-apocalyptic Earth
Most of the scripting is based on this game, so if my solution doesn’t quiet fit for you, give minilens a look yourself
This ended up being quiet long so i added a TL;DR at the bottom, if you know your way around Godot.
All right, let’s get on with it!
First of all we need to setup our project settings and our scene. I will just show the bare minimum scene structure.
In Project Settings->Display i set the width and height to my minimum size so i have a reference for setting up my scene correct. Again, it is just a reference so it is easy to visualize how much is visible at all times, we will manipulate the actual size of the viewport later in script.
Next up in Project Settings->Display we should set the stretch_mode to 2d and the stretch_aspect to ignore, have a look at http://docs.godotengine.org/en/stable/tutorials/engine/multiple_resolutions.html for more information on how stretching works.
That’s it for Project Settings.
Next up we add some stuff to our scene, we need a Control with a child Sprite.
The Texture on the sprite is our oversized background that goes beyond the minimum size we want, and the Sprite itself is positioned to the center of our minimum sized frame. For example check the Centered property in the Inspector and set the Position to (minimum width/2, minimum height/2).
The Control nodes anchor is set to Center so our background stays centered.
With our current project setup our image will just stretch and skew to whatever dimensions are needed to fill out the window, thanks to the stretch_mode and stretch_aspect.
Time to code!
This is where we set our minimum size that actual matters, the scaling will be based on the size we set here in the script, and not the Project Settings that we set earlier.
We will make use of the signal “size_changed” found on a Viewport (Viewport — Godot Engine (stable) documentation in English) so we don’t have to check for changes in screen size every frame, every second or whatever.
Whenever the screen size changes, we:
- Calculate a scale factor based on the minimum height and the current height of the screen
- Calculate a new size for the viewport
- Check if we should scale down (if the new width/height is smaller than our minimum width/height)
- Set the new size on our viewport
I wont explain the math itself, but it is really not that advanced and it should be easy to find examples and explanations around the web for simple scaling like this.
The script, again bare minimum.
onready var viewport = get_viewport()
var minimum_size = Vector2(1920, 1080)
viewport.connect("size_changed", self, "window_resize")
var current_size = OS.get_window_size()
var scale_factor = minimum_size.y/current_size.y
var new_size = Vector2(current_size.x*scale_factor, minimum_size.y)
if new_size.y < minimum_size.y:
scale_factor = minimum_size.y/new_size.y
new_size = Vector2(new_size.x*scale_factor, minimum_size.y)
if new_size.x < minimum_size.x:
scale_factor = minimum_size.x/new_size.x
new_size = Vector2(minimum_size.x, new_size.y*scale_factor)
Last up we have to save our script and then add it as a singleton in Project Settings → AutoLoad tab (http://docs.godotengine.org/en/stable/tutorials/step_by_step/singletons_autoload.html).
I just thought i would add a screenshot of a setup where there is a game node added, to show how we make sure our game stays centered on the background.
So we have a Main node that just contains the rest of our game.
Then we have two Control nodes, one which is our background from earlier, and the other Control node is where we place our actual game (I have just added the godot logo as a sprite as an example).
And to make sure our game stays at the same position, we set the GameControl Anchor to Center just like we did with the background.
And voila! we now have a setup where we can replace the SomeGameSprite with a Scene containing our actual game.
- In Project Settings->Display set stretch_mode = 2d and stretch_aspect = ignore.
- Create a Control and anchor it to Center, then add a Sprite as a child and set your oversized background as the texture and center it.
- Add the script, seen further up this answer, to AutoLoad
By the way this is a perfect example for a case where Autoload is the right solution.
As much as I criticize how most people use singletons I thought I should point that out.
It’s a good example, because the script doesn’t access any part of the scene tree, so it will work no matter what scene is the main scene, and it is by definition working on the application itself, so if you were to change the game so that the current main scene becomes a subscene of a new menu or something you would have to move this script to the new main scene if if wasn’t an autoload script.
Warlaan | 2016-11-16 10:33
I followed your instructions but I did not get.
Something is wrong, Sprite is not properly centered
Here are the results
P.S. Godot 2.1
dmklsv | 2016-11-17 16:57
My bad, Sprite has to be centered like this:
dmklsv | 2016-11-17 18:42
Ah yes i can see that it can be misunderstood, glad you got it working anyways.
I will edit my instructions to be more clear, hopefully.
Rasmus | 2016-11-18 07:13
Another way to center stuff is to calculate the translation required and using
viewport.set_global_canvas_transform to center. The advantage is it doesn’t require any “centered” Control containers (which I couldn’t get to work for some reason).
chanon | 2017-01-05 12:01
The idea of using the Control node is to let Godot’s anchoring handle those calculations.
But if you want to, it is pretty basic indeed, something like this would do:
But again, the idea was, why do this manually when Godot already does it for us?
If you want, i would be happy to help you out with the problems you have with the Control node.
Rasmus | 2017-01-05 12:19
Hey, this is working great, thanks. But what can I do if I have a 3D scene that I want to keep my minimum size? My UI Elements are scaling correctly, but my 3D seems not be be effected. I want my 3D scene to show everything it shows in the minimum size and show more of the scene instead. Any ideas?
TobiLa | 2018-03-30 08:15
This solution working well but issue with touch position when screen scale touch position is not scaling with it. Any fix for this issue?