@tool class_name PlayerCamera extends Node const CAMERA_PIXELS_PER_UNIT = 1.0 const CAMERA_PIXEL_SCALE = 1.0 @export var camera:Camera3D = null @export var target:Node3D = null @export var offset:Vector3 = Vector3(0, 0, 12) func _process(delta: float) -> void: if !camera || !target: return # I tried a few things but this is most consistent for both backbuffer and # framebuffer viewports. var viewportHeight = get_viewport().get_visible_rect().size.y; var unitScale = CAMERA_PIXEL_SCALE * CAMERA_PIXELS_PER_UNIT; var z:float = ( tan((deg_to_rad(180) - deg_to_rad(camera.fov)) / 2.0) * (viewportHeight / 2.0) ) / unitScale; var look = target.global_position; var position = offset + look; camera.look_at_from_position( Vector3(position.x, position.y + z, position.z), look ); pass