Parallax2D not working properly when Camera2D is attached with a script

Godot Version

Godot 4.5

Question

My Camera2D node is independent from the Player (they are both on the same level under the main scene). I wrote a script to make the camera follow the player in a specific way. The code uses ‘lerp’ to implement a “smart look-ahead” effect, allowing the camera to smoothly move ahead of the player’s direction of motion.

However, I’ve encountered issues when using Parallax2D for background (Sprite2D) scrolling. For example, when I set the scroll_scale to (0.01, 1), the background still moves horizontally very quickly, almost matching the player’s movement speed, and it also inherits the same “smart look-ahead” motion as the camera. When I set the scroll_scale to (- 0.1, 1), the background moves in the opposite direction, but still too fast.

Additionally, the background’s position sometimes appears off-screen when starting the game, even though I’ve set its global position to (0, 0). I have to manually reposition it every time I adjust the scroll_scale.

I’ve tried several approaches to fix this issue, but none of them work—unless I remove all the camera-related code. Interestingly, the background behaves correctly when I use the ParallaxBackground + ParallaxLayer combination. The issue only occurs when I switch to Parallax2D. I also tried to delete everything and set up Parallax2D freshly, together with the camera2D and the code, unfortunately the issue still exists.

Here I share the code for Camera2D

extends Camera2D
@export var target:CharacterBody2D

#special parametres
@export var base_offset: Vector2 = Vector2(0, -80)
@export var move_ahead: float = 80.0 # smart look-ahead
@export var look_up_offset: float = -60.0
@export var look_down_offset: float = 60.0
@export var ahead_smooth_speed: float = 8.0

var desired_position: Vector2
var current_ahead_offset: float = 0.0

func _ready():
enabled = true

find player node automatically

if not target:
target = get_tree().get_first_node_in_group(“player”)
if target:
print(“Camera2D found target”)
else:
print(“Camera2D cannot find target”)

func _physics_process(delta):
if not target:
return

desired_position = target.global_position + base_offset

var player_velocity = Vector2.ZERO
if “velocity” in target:
player_velocity = target.velocity
var target_ahead_offset: float = 0.0
if abs(player_velocity.x) > 5:
var speed_factor = min(abs(player_velocity.x) / 100.0, 1.5)
var dynamic_ahead = move_ahead * speed_factor
target_ahead_offset = sign(player_velocity.x) * dynamic_ahead

current_ahead_offset = lerp(current_ahead_offset, target_ahead_offset, ahead_smooth_speed * delta)
desired_position.x += current_ahead_offset

if Input.is_action_pressed(“look_up”):
desired_position.y += look_up_offset
elif Input.is_action_pressed(“look_down”):
desired_position.y += look_down_offset

global_position.x = lerp(global_position.x, desired_position.x, ahead_smooth_speed * delta)
global_position.y = lerp(global_position.y, desired_position.y, ahead_smooth_speed * delta)

It’d be easier if you provided the code in a codeblock or attached a small project. I was able to get your camera code working after some trial and error, but I’m not able to reproduce your issue. Would you be able to share a video or a small project so I could take a look?