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 (https://github.com/bojidar-bg) for helping me out on IRC by directing me to a previous game that has similar behavior, https://github.com/KOBUGE-Games/minilens
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 (http://docs.godotengine.org/en/stable/classes/class_viewport.html#signals) 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