Ways to make camera follow remote node?

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

Working on a project to learn the ropes in Godot, full source available here.

A huge part of this project has been figuring out how to use Godot’s viewport and camera-related nodes, as well as how to manage the scene tree effectively. The following camera scene evolved out of my desire to implement cinematic scene transitions (in addition to normal camera stuff like following the player):

camera scene tree

The “scene being filmed” (which for the purposes of this question can be assumed to include an instance of the player scene) is added at runtime as a child of the “set” node (like, as in a film or theater set, that’s how my brain is working at the moment).

Scrolling, following, and screen-level effects (such as fading in/out or tinting the canvas) are all handled through this scene, so it seems like my Camera2D is pretty much stuck there in the node hierarchy, where you see it in the image.

So we get to camera following. Godot’s Camera2D node handles this automatically based on the position of its parent node, thus the “aim” node. So how to move the aim node based on something outside this scene hierarchy?

Right now it’s enough for the player scene to have a child RemoteTransform2D, and set/unset it to target the aim node whenever the camera needs to follow/unfollow:

player scene tree

…and in the camera’s script (scenes/misc/jCam2D.gd, note that I tried to arrange the following excerpt for readability, not by actual line number):

onready var cam2d = find_node("Camera2D")
...
func follow(target: CanvasItem, cell: Area2D, horizontal_buffer: int=0, vertical_buffer: int=0):
  unfollow()
  FOLLOW_TARGET = target.find_node("RemoteTransform2D")
  FOLLOW_CELL = cell
  HORIZONTAL_BUFFER = horizontal_buffer
  VERTICAL_BUFFER = vertical_buffer
  FOLLOWING = true
  FOLLOW_INITIALIZED = false

func unfollow():
  if FOLLOWING and FOLLOW_INITIALIZED:
    FOLLOW_TARGET.remote_path = ""
    FOLLOW_TARGET = null
    FOLLOW_CELL = null
    HORIZONTAL_BUFFER = 0
    VERTICAL_BUFFER = 0
    FOLLOWING = false
    FOLLOW_INITIALIZED = false
    aim.position = cam2d.get_camera_screen_center()
    cam2d.current = false

# the following block occurs in _physics_process
...
elif FOLLOWING and not FOLLOW_INITIALIZED and not get_tree().paused:
    FOLLOW_TARGET.remote_path = "../../../../aim"
    var shape = FOLLOW_CELL.find_node("CollisionShape2D").shape
    cam2d.limit_left = FOLLOW_CELL.position.x - shape.extents.x - HORIZONTAL_BUFFER
    cam2d.limit_right = FOLLOW_CELL.position.x + shape.extents.x + HORIZONTAL_BUFFER
    cam2d.limit_top = FOLLOW_CELL.position.y - shape.extents.y - VERTICAL_BUFFER
    cam2d.limit_bottom = FOLLOW_CELL.position.y + shape.extents.y + VERTICAL_BUFFER
    cam2d.current = true
    FOLLOW_INITIALIZED = true
...

But I’m not sure how I feel about RemoteTransform2D as a long-term solution. Every potential camera target would need to have a dedicated RemoteTransform2D node with generally the same characteristics. You can’t just pick any old RemoteTransform2D, because the target might have other RemoteTransform2D’s that it uses for other purposes. I could have the camera add a RemoteTransform2D at run-time, but…it seems like that might require an overly-intimate knowledge of how the target handles its children. I could have every potential target inherit from a class that implements the “follow-able” characteristics, but…not only might that be overkill if there are alternate solutions, I’m not even sure I fully grasp the code requirements for it. Does GDScript have the idea of “interfaces”? (apparently not)

So that leaves me wondering if I just need to learn a bit more GDScript to implement an object-oriented solution, if there’s a simpler alternative that I don’t know about, or if (worst-case) I’ve “painted myself into a corner” with the way I’ve constructed the camera scene. Thoughts? Perhaps links to tutorials that I may have missed? The Godot camera tutorials I know of don’t ever get this in-depth.

P.S. In anticipation of the question “why did’t you make the Camera2D a child of the player” (which is what I see in most tutorial videos), the answer was hinted at earlier: cinematic transitions. Specifically I was trying to figure out how to dissolve between scenes, and so far the only way I’ve been able to do that is…without getting into too much detail, by structuring the camera scene in such a way that I can control render post-processing while loading a new scene. And yes, I got it “working” (you can also see a brief demo of my current following implementation in the same video). So any suggested solution that involves making the Camera2D a child of the player will need to take that into account.

P.P.S. This is my first post. I dunno if it’s easier for people to just clean it up themselves and move on, but regardless, I would greatly appreciate suggestions for improving it. Yes I have read the guidelines.