+4 votes

Basically it's a background which moves with your mouse.

I would like to know how can I make it so I can enhance my game.

in Engine by (23 points)

1 Answer

+5 votes

I just wanted to make the same effect, but couldn't find something on the internet, so I am writing a little tutorial here, it is not that hard to make.

Lets set up the nodes first, you need a ParallaxBackgroundand two ore more ParallaxLayer as child nodes and each ParallaxLayer needs a Sprite as child. Give the first Spritethe background texture, the second Spritethe foreground texture.

Next you need to get the x and y coordinates of your mouse, that is possible with

func _input(event):
    if event is InputEventMouseMotion:
        var mouse_x = event.position.x
        var mouse_y = event.position.y

Next you want to map the mouse coordinates on the screen between the intervall
[-1 1].
For this you need the size of your screen, you can get that with get_viewport.size(), there are many other ways to get the size of your screen, you may use what suits you best.
Then you can do

var relative_x = (mouse_x - (viewport_size.x/2)) / (viewport_size.x/2)
var relative_y = (mouse_y - (viewport_size.y/2)) / (viewport_size.y/2)

Now you have the positon of your mouse relative to the middle of the screen, which is 0 in the middle 1 at the far right/down and -1 at the far left/up, perfect!
Whats left to do is to manipulate the position of the ParallaxLayers, just get the node for each ParallaxLayer and set the motion_offset property like this:

ParallayLayer.motion_offset.x = multiplier * relative_x
ParallayLayer.motion_offset.y = multiplier * relative_y

multiplier is a value that amplifies the parallax effect, use smaller values for background layers and greater values for foreground layers, but it should be at least 2, to have a visible effect.
So thats it!

The ParallaxLayers need to update every time the mouse moves so all that code goes into the func _input(event):
To avoid that part of your screen goes blank, make the textures for the background larger than the screen.
Here is how it can look like with 7 layers:
GIF

Props to LuminousDragonGames for the parallax images
https://opengameart.org/content/parallax-space-scene-seamlessly-scrolls-too

by (24 points)
edited by

Oh my God I can't thank you enough for this!!
I've been trying to achieve this effect since so long, but couldn't find anything on the internet as well. I'm not good at math programming so I couldn't do it myself...
You're a lifesaver!

Hello. Would you please be able to show me the script that you have for this? I'm not quite able to figure it out

No problem, here is the code:

extends ParallaxBackground

var viewport_size
var relative_x
var relative_y

func _ready():
    get_tree().get_root().connect("size_changed", self, "viewport_changed") # register event if viewport changes
    viewport_changed()
    relative_x = 0
    relative_y = 0

func _input(event):
    if event is InputEventMouseMotion:
        var mouse_x = event.position.x
        var mouse_y = event.position.y
        relative_x = -1 * (mouse_x - (viewport_size.x/2)) / (viewport_size.x/2)
        relative_y = -1 * (mouse_y - (viewport_size.y/2)) / (viewport_size.y/2)
        # print("relative_y: " + str(relative_y))
        # print("relative_x: " + str(relative_x))
        var count = 4
        for child in self.get_children(): # for each parallaxlayer do...
            child.motion_offset.x = count * relative_x
            child.motion_offset.y = count * relative_y
            count = count * 1.6

# gets called on the start of the application once and every time the viewport changes
# centers the images
func viewport_changed():
    viewport_size = get_viewport().size
    for child in self.get_children():
        child.get_node("Sprite").offset.x = viewport_size.x / 2
        child.get_node("Sprite").offset.y = viewport_size.y / 2

Don't forget you need the tree to look like this

-AnyNode
  -ParallaxLayer Background [script]
      -ParallaxLayer0
          -Sprite
      -ParallaxLayer1
          -Sprite
      -ParallaxLayer2
          -Sprite
      -ParallaxLayer3
          -Sprite
etc...

Hope it helps!
If you have any questions, let me know :)

It says Invalid get index 'offset' (on base: 'null instance')

Seems like one of your parallaxlayer has not a sprite as a child, or you have other nodes as child of parallaxBackground.
Make sure that all childs of parallaxBackground are parallaxLayers and each parallaxLayer has a Sprite as a child.
Because I used child.get_node("Sprite") each Sprite of a parallaxLayer has to have the name Spriteotherwise it will not find the right node.

Can it do it on 3d?

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 Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.