488 words
2 minutes
Parallax Camera Tutorial for Godot 4
Godot has a built in parallax system, but only for 2D games, so we want one that can work in both 2D and 3D. Plus, we have more customizable control over it!
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()
Resources: