How to center ParallaxBackground inside a Control with anchors set to Center?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Mike Gringauz
:warning: Old Version Published before Godot 3 was released.

Hi!

In general…

When I place a Sprite inside a Control with all anchors set to Center it works fine when the game window size changes, that is the Sprite stays in the center of the screen.

How to achieve the same centering of ParallaxBackground inside a Control? It seems to stay top-left anchored…

To be concrete…

I have the following scene hierarchy:

Also I run the following function on viewport “size_changed” signal to keep game screen responsive to fit multiple resolutions as described in QA https://forum.godotengine.org/9947/responsive-to-fit-multiple-resolutions:

func on_viewport_size_changed ():
			var size = OS.get_window_size()
			scale_factor = SAFE_SIZE.y / size.y
			window_size = Vector2 (size.x * scale_factor, SAFE_SIZE.y)
			if window_size.x < SAFE_SIZE.x:
				scale_factor = SAFE_SIZE.x / window_size.x
				window_size = Vector2 (SAFE_SIZE.x, window_size.y * scale_factor)
			if window_size.y < SAFE_SIZE.y:
				scale_factor = SAFE_SIZE.y / window_size.y
				window_size = Vector2 (window_size.x * scale_factor, SAFE_SIZE.y)
			viewport.set_size_override( true, window_size )

And it seems to work fine with “ordinary” sprites, that is ship-sprite and background-sprite stays always centered inside a Control:

No matter how window size have changed:

But not with parallax background which stays always top-left anchored even if put inside a Control with anchors set to Center:

No matter how window size have changed:

Thanks!

Why are you using a parallax background (which is a CanvasLayer) inside a Control? I’m not sure it was ever intented to do that.

Zylann | 2016-12-02 11:34

I’m placing ParallaxBackgrounf inside a Control because I want this behavior in my game: https://forum.godotengine.org/9947/responsive-to-fit-multiple-resolutions

Mike Gringauz | 2016-12-02 11:50

I still don’t get why you want parallax background inside a Control (I see none in the example you gave). Is that a tiled background? Do you have a screenshot?

Zylann | 2016-12-02 18:23

Added screenshots and more details.

Mike Gringauz | 2016-12-09 07:56

You need control because parallax layers ignore camera zoom while dealing with multiple resolutions. Putting it inside a control works as workaround. I don’t know if it behaviour is intended or not, but it is very annoying.

lukas | 2016-12-09 09:11

Seems like ParallaxBackground works against the rest of Godot’s principles.

Mike Gringauz | 2016-12-09 12:04

:bust_in_silhouette: Reply From: Warlaan

The parallax background is supposed to be a background (hence the name :-P), so it’s always going to fill the entire viewport, which by default is the whole window.
In order to restrict the area that is to be filled place the ParallaxBackground inside a Viewport node inside the Control.
The Control will define the extends of the Viewport and the Viewport will define the extends of the ParallaxBackground.

Note that for the parallax effect to work you then need to place the camera inside the viewport as well, so I recommend building the whole game as a separate scene and placing that inside the viewport so it gets centered and cropped.

Warlaan | 2016-12-09 09:22

Can’t get it…
How do I setup up the scene hierarchy in such a way?
Or maybe Godot has something similar to ParallaxBackgroud, but behaves as a “regular” Node2D that I can just put inside a Control and it will be centered according to anchors?

Mike Gringauz | 2016-12-09 09:47

Like this:

The viewport just restricts the area that the engine draws into. Since you want to have a small drawable area centered inside the window rather than small graphics centered inside a large drawable window you need to insert a viewport node in between your topmost centered control and everything that’s inside it.

Warlaan | 2016-12-09 10:02

I’ve added your snippet as follow:

Now the parallax background stays centered during window resize but I have the following issue:
When I leave my old camera as current, the parallax doesn’t scroll…
When I set camera1 as current, the parallax doesn’t scroll and the player node (ship) is not tracked any more by a camera and as it moves further it left the game window…

How to handle cameras so that camera follows the player node (ship) and the parallax do scroll?

Mike Gringauz | 2016-12-09 16:13

The viewport is basically like a TV showing a football game. If you move the camera that is a child to the viewport it’s like moving the camera that films the game. If you move the camera that is outside the viewport it’s like moving your head so you see the TV out of the corner of your eye.
There’s a good chance that there’s no option to activate one camera “inside” the viewport and another one outside of it, but luckily you don’t need that for your game, since the game area is meant to always be in the center of the screen.
So move all controls inside the viewport and use the camera the same way you used it before.

If you are using the outer camera to position the controls on the screen then just change the root note to a canvas layer instead and remove the outer camera.

Warlaan | 2016-12-09 17:34

So move all controls inside the viewport and use the camera the same
way you used it before.

But as I read in documentation, there is already a viewport (root) in each and every scene tree:
enter image description here
Why should I use a viewport inside a viewport then?

If you are using the outer camera to position the controls on the
screen then just change the root note to a canvas layer instead and
remove the outer camera.

I’m using the “outer” camera, as my main camera to track player (ship)

Mike Gringauz | 2016-12-12 17:06

There is a default viewport that covers the whole window, but in your case you don’t want your game to cover the whole window, you want it to be centered inside the window.
A parallax background is built to cover the whole viewport it is in. If you use the default viewport the parallax background fills the whole window, resp. starts filling it beginning in the top left corner (as you can see in your screenshots).
So you need an outer viewport in which you place the game and an inner viewport that represents the game space which is the area that should be filled by the parallax background.

You need to separate the game from the construct that places it in the middle of the screen. That’s why I was talking about the outer and the inner camera. The outer camera just sees the game, it doesn’t know anything about what’s going on in there. The inner camera just sees everything that’s happening inside the game, it doesn’t know how to place anything in the center of the screen.

Warlaan | 2016-12-12 17:28

A parallax background is built to cover the whole viewport it is in.

But I can see that it’s not quite true when the root viewport size changed, that is when the window resized. Isn’t parallax background recalculate itself accordingly?

Sorry I’m noobness, but I can’t understand your point. Can you please explain me “on fingers” how one could anchor ParallaxBackground to center of the screen?

If you use the default viewport the parallax background fills the
whole window, resp. starts filling it beginning in the top left corner
(as you can see in your screenshots).

Can I configure ParallaxBackground somehow to start filling from center to sides or something?

Mike Gringauz | 2016-12-12 17:50

:bust_in_silhouette: Reply From: lukas

ParallaxBackground doesn’t for some reason scale if you deal multiple resolutions with setting camera zoom. Try to use different scene hierarchy:

While changing resolution you need to change scale and position of the Control and mirroring of ParallaxLayer. I am using the following script attached to each ParallaxLayer node:

extends ParallaxLayer

func _ready():
	var offset = get_node("/root/global/resolution").parallax_offset
	var scale = get_node("/root/global/resolution").viewport_scale
	# Changing scale of the layer:
	scale = 1.0 / float(scale)
	get_node("Control").set_scale(Vector2(scale, scale))
	# Mirroring has to be scaled:
	self.set_mirroring(self.get_mirroring() * scale)
	# Changing position of the layer:
	# (Offset depends on motion_scale of the layer)
	var motion_scale = self.get_motion_scale()
	var pos = Vector2(offset.x * (1 - motion_scale.x), offset.y * (1 - motion_scale.y))
	get_node("Control").set_pos(pos)

The script of singleton resolution (script where parallax_offset and viewport_scale) looks like:

extends Node

const BASE_WIDTH = 1280.0
const BASE_HEIGHT = 720.0
const BASE_ASP = 1.7777778
var viewport_res = Vector2(BASE_WIDTH, BASE_HEIGHT)
var asp = 1.7777778
var viewport_scale = 1.0
var parallax_offset = Vector2(0.0, 0.0)

func _ready():
	viewport_res = get_node("/root").get_children()[1].get_viewport_rect().size
	asp = viewport_res.x / viewport_res.y
	var mult = 1.0
	if asp > BASE_ASP:
		mult = asp / BASE_ASP
	viewport_scale = BASE_WIDTH / viewport_res.x * mult
	parallax_offset.y = (viewport_res.y - BASE_HEIGHT / viewport_scale) / 2.0
	parallax_offset.x = (viewport_res.x - BASE_WIDTH / viewport_scale) / 2.0

Hi, where do I put ParallaxBackground from your example in my scene’s hierarchy?

Mike Gringauz | 2016-12-09 11:57

As child of base_node.

lukas | 2016-12-09 13:32

Doesn’t work, probably did something wrong.
Such a simple task - keep parallax centered - made so hard…

Mike Gringauz | 2016-12-09 16:16

Hmmm. Strange. It works for me. Only difference I see is that my base node is Node2d and not Node.

lukas | 2016-12-09 16:24

Can you share a sample project?

Mike Gringauz | 2016-12-09 16:29

Maybe later when I have time to make a simple sample project :wink:

lukas | 2016-12-10 09:56

As simple as you can! :slight_smile:

Mike Gringauz | 2016-12-10 10:18