Read the original post on my blog for a smoother experience.
Sudo Code:
Parallax Controller
- Stores reference to the camera
- Stores an array of all the affected background layers
Parallax Layer
- Placed on the background layer to move
- Contains variables to control what kind of movement it does:
- up and down
- left and right
- even depth! (forward and back)
Godot Tutorial
3D
Parallax Controller
- First, we want to store a list of all our layers affected by the parallax as well as a reference to the camera
- Then, as long as it’s enabled, we want the parallax layers to move with the camera by calling the _move_parallax_layers() function in the _process() function
- Inside our _move_parallax_layers() function we:
- Get the cameras updated position
- Check that the camera has moved at all and exit early if it hasn’t
- Get the direction the camera moved in
- Call each layer to move based on the cameras direction
- Save the cameras position for next frame
extends Node3D
class_name ParallaxController
# Variables
@export var parallaxLayers : Array[CustomParallaxLayer]
@export var camera : Camera3D
var enabled : bool = true
var lastCameraPos : Vector3 = Vector3.ZERO
func _process(delta):
if enabled:
_move_parallax_layers()
func _move_parallax_layers() -> void:
var newCameraPos : Vector3 = camera.global_position
if newCameraPos - lastCameraPos == 0:
return
var direction : Vector3 = lastCameraPos.direction_to(newCameraPos).normalized()
for layer in parallaxLayers:
layer.move(direction)
lastCameraPos = newCameraPos
Parallax Layer
- Here we store the parallax speed we want as well as what directions we want it affected by (This system supports all axis so you can make some cool effects!)
- The move() function takes in the direction of the camera and we use that to calculate which direction the parallax should happen in (We subtract since we want it to move in the opposite direction of the camera)
extends Sprite3D
class_name CustomParallaxLayer
# Variables
@export var parallaxSpeed : float
@export var moveLeftRight : bool = true
@export var moveUpDown : bool = false
@export var moveDepth : bool = false
func move(_direction : Vector3) -> void:
if moveLeftRight:
global_position.x -= _direction.x * parallaxSpeed * get_process_delta_time()
if moveUpDown:
global_position.z -= _direction.z * parallaxSpeed * get_process_delta_time()
if moveDepth:
global_position.y -= _direction.y * parallaxSpeed * get_process_delta_time()
2D
- Our 2D system works the same way, but without the extra axis
Parallax Controller
extends Node2D
class_name ParallaxController
# Variables
@export var parallaxLayers : Array[CustomParallaxLayer]
@export var camera : Camera2D
var enabled : bool = true
var lastCameraPos : Vector2 = Vector2.ZERO
func _process(delta):
if enabled:
_move_parallax_layers()
func _move_parallax_layers() -> void:
var newCameraPos : Vector2 = camera.global_position
if newCameraPos - lastCameraPos == 0:
return
var direction : Vector2 = lastCameraPos.direction_to(newCameraPos).normalized()
for layer in parallaxLayers:
layer.move(direction)
lastCameraPos = newCameraPos
Parallax Layer
extends Sprite2D
class_name CustomParallaxLayer
# Variables
@export var parallaxSpeed : float
@export var moveLeftRight : bool = true
@export var moveUpDown : bool = false
func move(_direction : Vector2) -> void:
if moveLeftRight:
global_position.x -= _direction.x * parallaxSpeed * get_process_delta_time()
if moveUpDown:
global_position.y -= _direction.y * parallaxSpeed * get_process_delta_time()
You can support me by buying the project files on my itchio if you’d like. You can read more godot tutorials here on medium or on my blog (which as better formatting so I recommend it!)