Look ahead for Camera2D on Platformer

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By JayH

Hi,

I’m trying to improve the Camera2D settings, as when my platformer character moves, the camera lags behind the player. Instead it’s typical to have the camera looking ahead in the direction the Player is facing. Otherwise you can’t see what is coming from that direction easily.

Node set up:

Level_01
—Player
------Camera2D

The Camera2D node is a child of the Player node.

I used ChatGPT to help create the code, but it doesn’t seem to work as the camera seems to travel off diagonally to the right, although logicaly it seems that it should work:

Here’s the script:

extends Camera2D
  
var look_ahead = 20 # The number of pixels to look ahead

@export var myPlayer: Node

func _process(delta):
	# Calculate the position to move the camera to
	
	var target_pos = myPlayer.global_position + Vector2(look_ahead, 0)
  
	# Move the camera towards the target position
	position = position.lerp(target_pos, delta * 2.0)

I’m assuming that in Godot 4, using the below export method is the correct method to get the Player node, it doesn’t throw any errors:

@export var myPlayer: Node
:bust_in_silhouette: Reply From: spaceyjase

godot’s camera has an offset for this; just give it a direction and lerp the value:

func _process(delta: float) -> void:
    offset = offset.lerp(target_offset, delta * lerp_speed)

Assuming the camera moves with the target (or is a child). target_offset here could be the linear_velocity of the target (for example).

Thanks for the reply, could you explain a little further how I can implement your code please?

Where you mention target_offset could be the linear_veocity, I’m not sure exactly whay you are referring to.

As I mentioned in my previous post, my Camera2D node is a child of the Player node.

JayH | 2023-06-24 12:49

I can try although it really depends on how you’re moving the player. How you implement this is really based on what you’ve already written and your project; I can’t provide exact code. I’m going to make a few assumptions so I apologise if they don’t make much sense :slight_smile:

I assume you have some input and apply the input direction to the player (perhaps a CharacterBody2D or similar) to move them. This will give the body a velocity, its direction of movement (as a Vector). This velocity can be used to set the camera offset and ‘look ahead’ along the direction of movement.

In this game, the camera has a method to change a variable target_offset that, in _process (as posted), then lerps the offset towards the target (so it is smooth rather than a jump).

spaceyjase | 2023-06-24 13:16

Hi,

Thanks again for the info, it’s a little clearer, but I’m still not sure where to go next.

This is my script for the Player and yes it uses CharacterBody2D.

extends CharacterBody2D


const SPEED = 150.0
const PLAYERSPEED = 150.0
const JUMP_VELOCITY = -350.0
const JUMP_LOW = -200
var onGround = false


# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")

#@onready player_anim_sprite = $AnimatedSprite2D # Get Player's AnimatedSprite2D Reference



func _physics_process(delta: float) -> void:
	# Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta
		if (velocity.y >= 0):
			$AnimatedSprite2D.play("Jump") #Play the Player's Fall Animation
		else:
			$AnimatedSprite2D.play("Fall") #Play the Player's Jump Animation
		
		
	else:
		if (velocity.x == 0):
			$AnimatedSprite2D.play("Idle") #Play the Player's Idle Animation
		else:
			$AnimatedSprite2D.play("Walking") #Play the Player's Walk Animation
			
			
			
			
	# Handle Jump.
	if Input.is_action_just_pressed("ui_up") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# VARIABLE JUMP HEIGHT		
	if Input.is_action_just_released("ui_up"):
		velocity.y = -JUMP_LOW




# MOVE PLAYER LEFT/RIGHT
	if Input.is_action_pressed("ui_right"):
		velocity.x = PLAYERSPEED
		$".".scale.x =  scale.y * 1 #Flip Player and CollisionShape2D via Scale Right
		
		#$AnimatedSprite.flip_h = false
	elif Input.is_action_pressed("ui_left"):
		velocity.x = -PLAYERSPEED
		$".".scale.x =  scale.y * -1 #Flip Player and CollisionShape2D via Scale Left
		
	else:
		velocity.x = 0
		if onGround == true: #&& HammerTime == false:
			$AnimatedSprite.play("Idle")



	move_and_slide()

The Camera2D script is pretty much empty currently, with just a reference for the Player node:

extends Camera2D
  
@export var myPlayer: Node


#func _process(delta: float) -> void:
#	offset = offset.lerp(target_offset, delta * lerp_speed)

For the Camera2D script, could you show me how to implement your suggestion please? It would be handy for me to learn how it’s actually done correctly, especially as I’m still very new to Godot.

Thanks.

Edit: I didn’t think I’d understand how to implement it, however I just added a variable for the lerp_speed and it seems to make the camera look forward, however I don’t think it is doing what I had hoped, perhaps my explanation wasn’t great.

The camera immediately shunts forward (with lerp), then snaps (with lerp) back to the original position.

This would be great like in Sonic The Hedgehog, where Sonic is able to look up or down to see the level without moving, but not ideal for horizontal movement.

Instead I want the camera to always look ahead a certain amount horizontaly, depending which way the Player is facing without springing/snapping back to the original position.

This will allow the Player to see what is coming more easily (enemies etc). Currently the Player moves across the screen a certain amount before the camera follows as there is a dead zone (drag), I’d like to keep the dead zone. Having the camera at the centre more so than the Player would be better.

In essence, an offset look-ahead for the camera that is perminent :wink:

JayH | 2023-06-24 16:55

A first stab that should move you in the right direction (ahem) is to change target_offset in the comment to myPlayer.velocity:

offset = offset.lerp(myPlayer.velocity, delta * lerp_speed)

You should define a lerp_speed:

@export var lerp_speed: float = 1.0

And change it in the editor to your liking.

spaceyjase | 2023-06-24 17:26

Hi,

Thanks again, it’s possible that when I added the edit in my post, you were already editing your reply :wink:

I got it to work the way you suggested, it was easier than I thought, however it isn’t the correct look-ahead I was looking for, as I need it to be preminent and not for it to lerp back to the original position.

From reading the docs it should be possible to use set_h_offset to change the camera’s position, which does work, however I don’t know how to do it for both directions. I’m missing some syntax that allows me to include the Players direction /velocity.

Formatting syntax is my biggest problem in Godot (and Unity)!

Can you help further?

Thanks :wink:

JayH | 2023-06-24 18:25

That’s good. The lerp is an example here; you can change the offset directly and it’ll snap to whatever vector offset is set. Experiment and tweak to the desired result. Good luck!

spaceyjase | 2023-06-24 21:12