How to stop camera "lag"?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By ledi51
:warning: Old Version Published before Godot 3 was released.

Hi, I’ve been learning how to use Godot recently but I can’t seem to find a solution for this particular problem.

The Camera2D node seems to update its position a frame after the object it is following has moved, no matter what I do. I tried making it a child of the node I want it to follow. I tried making it NOT a child but using code to make it update its position at the exact same time that the position of the node it is following is updated. No matter what, the “character” object draws at its new position before the camera actually moves, which means the character is no longer centered on the screen as long as it is moving. It creates a camera lag that is one frame behind the player at all times. Is there something I am missing here? Or some work around?

Thanks!

EDIT: I’ve found this code snippet to be quite revealing:

get_parent().get_node("Camera2D").set_pos(position + direction*speed*delta)
print("CAMERA NODE POSITION")
print(get_parent().get_node("Camera2D").get_pos())
print("'CAMERA' POSITION")
print(get_parent().get_node("Camera2D").get_camera_pos())

Result:

CAMERA NODE POSITION
426.538422,262    <--- Correct
'CAMERA' POSITION
413.232819,262    <--- A frame behind

Thoughts?

:bust_in_silhouette: Reply From: genete

Try to set_drag_margin to 0.0 in all directions.

Thanks for your response. When I tried setting the camera as a child of the player, I also turned off hdrag and vdrag, but just in case I just now tried changing all the margins to 0 and tried it with drag off and with drag on, but the problem persists in all cases.

ledi51 | 2016-07-18 11:41

:bust_in_silhouette: Reply From: Zylann

Chances are your character is moving in _fixed_process() while the camera moves in _process(). Those two aren’t called at the same rate, causing potential delay for the camera. If that’s the case, you could try to set the camera’s position in_fixed_process() too.

If you are only using _process(), make sure the Camera is updated after the player moves: you can set the camera’s position just after setting the player’s position, or move the camera below the player in the scene tree.

My character moves using set_pos() inside of _process():

func _process(delta):
var position = get_pos()
var direction=Vector2(0,0)
if (Input.is_action_pressed("ui_right")):
	direction += Vector2(1, 0)
elif (Input.is_action_pressed("ui_left")):
	direction += Vector2(-1,0)

if (Input.is_action_pressed("ui_up")):
	direction += Vector2(0,-1)
elif (Input.is_action_pressed("ui_down")):
	direction += Vector2(0, 1)
	
if (direction != Vector2(0,0)):
	direction = direction.normalized()
	
	set_pos(position + direction*speed*delta)

I tried moving the camera in _fixed_process() but no dice. Perhaps it is an issue with when set_pos() actually takes effect?

ledi51 | 2016-07-18 22:24

I used to use GameMaker a long time ago, and this was a really common newbie problem with GM. But the fix was easy – all you had to do was make sure the camera updated its position after the player moves but before the screen gets redrawn. I have no idea how to do that in Godot because I don’t know if there is a way to control the order of these events.

ledi51 | 2016-07-18 22:30

Oh, so in that case you can do this:

# Set the player's position
set_pos(position + direction*speed*delta)

# Set the camera's position after so it will have the correct target
# (you have to get the camera beforehands)
camera.set_pos(get_pos())

Another trick can be to move the camera below the avatar in the scene tree, so it will be updated afterwards (not sure about this though).

Zylann | 2016-07-18 22:39

Unfortunately that’s one of the things I tried initially. However, I noticed in the documentation that there was a special get_camera_pos() member of Camera2D. After playing around I have narrowed down the problem I think:

    get_parent().get_node("Camera2D").set_pos(position + direction*speed*delta)
	print("CAMERA NODE POSITION")
	print(get_parent().get_node("Camera2D").get_pos())
	print("'CAMERA' POSITION")
	print(get_parent().get_node("Camera2D").get_camera_pos())

Result:

CAMERA NODE POSITION
426.538422,262    <--- Correct
'CAMERA' POSITION
413.232819,262    <--- A frame behind

So the position of the camera node is being updated immediately, but the position of the actual camera (viewport) is clearly not being updated until the next frame, after everything has already been drawn.

Hopefully there is a way to fix this short of altering the godot source code? I wouldn’t even know where to look… haven’t had the pleasure of diving into that just yet.

ledi51 | 2016-07-18 23:59

After toying a bit, putting the camera as child of the player and moving the player inside _fixed_process() did the trick for me.

Indeed, the fact the camera is always updated late no matter what using _process() is weird Oo

Zylann | 2016-07-19 00:33

Hey, that’s good to know! I also found that Godot 2.1 has a built in method to manually update the camera view position to match the camera node position called align(), so now there are two ways to get around this :slight_smile:

ledi51 | 2016-07-19 00:56

Well, the align() function I made was intended to recenter the camera’s position. I don’t think it updates the transform immediately.

timoschwarzer | 2016-07-20 20:40

    print("Position player:")
	print(global.current_scene.get_node("player").get_pos())
	print("Position camera (before align):")
	print(get_camera_pos())
	align()
	print("Position camera (after align):")
	print(get_camera_pos())

Result:

Position player:
1092.415039,262
Position camera (before align):
1078.603882,262
Position camera (after align):
1092.415039,262

It’s interesting that this wasn’t an intended consequence, but it does indeed update the camera position immediately.

ledi51 | 2016-07-20 21:40

:bust_in_silhouette: Reply From: ledi51

RESOLVED!

Hey guys, I was digging through the source code to add my own method to update the camera view position when I noticed that a method for this was added for Godot 2.1 beta. I was using 2.0.4 when I had this problem. Now I am using 2.1 and I can just use align() on the camera2d node to synchronize the camera view with the node position.

I may still alter the source code to handle this automatically…

Thanks for your help!

Almost broke my brain, trying to solve the exact same problem in 2018. Somebody should really step up their fixing process)

Dmitriy Romanov | 2018-06-06 17:44

:bust_in_silhouette: Reply From: salmalec

In Godot 3.2, when making the Camera2D a child node of the object I want it to follow, I can run force_update_scroll() on the camera to force updating its position right away instead of one frame later.

func _process():
    self.position.x += 10 # changing the position of the object
    $Camera2D.force_update_scroll() # forcing camera repositioning
    var cam_pos = $Camera2D.get_camera_actual_position() # now the position is correct

Works with drag margins too.