After some investigation and assistance from
crazy_stewie
, we managed to determine
the cause of the issue.
First, we disabled the render server using
RenderingServer.render_loop_enabled = false
when we started the transition and drew the next
frame using
RenderingServer.force_draw.call_deferred()
.
This helped us confirm that the frame was being
rendered incorrectly for the next frame, sometime
between the transition starting and when the camera
position was set.
Adding a breakpoint, we were also able to confirm that the camera position was set correctly during that frame as well, but it was still rendering in the incorrect position.
We also used the following code to see when the frames were drawn:
func init()
connect(frame_pre_draw)
RenderingServer.frame_pre_draw.connect(frame_post_draw)
RenderingServer.frame_post_draw.
func frame_pre_draw():
print("Pre Draw")
func frame_post_draw():
print("Post Draw")
This, coupled with other debugging prints helped point out the fact that there was a frame being rendered with the position set to the correct value, but still rendering in the incorrect place.
As it turns out, as crazy_stewie
explained, the camera can be thought of as having
two different representations; the camera is just a
fancy interface to a viewport in the
RenderingServer
, which does the actual
rendering. The function to force the camera to use
the new position is align()
. Calling
this function after setting the position fixes the
issue.
Inspecting the code, it appears that
align()
takes the global position into
account when updating its internal representation
before calling update_scroll()
which
updates the viewport. Adding a debugging print for
the update_scroll
function also reveals
that it is called before the frame is rendered, but
since it doesn't take the global position into
account, the viewport is not updated correctly.
Calling align()
fixes the problem.
Consider the following code:
func update_position():
if target:
= target.global_position + Vector2(0, -100)
global_position print("Setting camera position to ", global_position)
print("Smoothing enabled? ", position_smoothing_enabled, "; Target position: ", get_target_position())
align()
print("Smoothing enabled? ", position_smoothing_enabled, "; Target position: ", get_target_position())
func _init():
connect(frame_pre_draw)
RenderingServer.frame_pre_draw.connect(frame_post_draw)
RenderingServer.frame_post_draw.
func frame_pre_draw():
print("Pre Draw")
func frame_post_draw():
print("Post Draw")
func _notification(what: int):
# This notification will call `update_scroll()`. No other notification calls `align()`
# See https://github.com/godotengine/godot/blob/a8453cb3337c2e27c061a385f9c772cf670e38e0/scene/2d/camera_2d.cpp#L230-L234
if what == NOTIFICATION_TRANSFORM_CHANGED:
print("update_scroll")
This results in the following (abridged) log:
Setting camera position to (-120, -73)
Smoothing enabled? false; Target position: (136, -49)
Smoothing enabled? false; Target position: (-120, -73)
update_scroll
Pre Draw
Post Draw
As you can see, the target position isn't updated
unless we call align
even though we set
the position and update_scroll
, which
updates the viewport in the
RenderingServer
, will use the incorrect
position (the target position) if we don't call
align.