class_name Camera extends Camera3D @export_flags_3d_physics var collision_mask := 1 @export_group("Zoom") @export var zoom_speed := 0.5 @export var zoom_interpolation := 10.0 @export var zoom_min := 2.0 @export var zoom_max := 8.0 var target_distance: float var occlusion_distance: float = far var pivot: Node3D func _ready(): target_distance = position.z pivot = get_parent() Global.camera = self func _input(event): if event.is_action_pressed("zoom_in", true): target_distance -= zoom_speed on_distance_changed() get_viewport().set_input_as_handled() if event.is_action_pressed("zoom_out", true): target_distance += zoom_speed on_distance_changed() get_viewport().set_input_as_handled() func on_distance_changed(): target_distance = clampf(target_distance, zoom_min, zoom_max) Global.camera_attributes.dof_blur_far_distance = target_distance + 1.0 func _process(delta): var distance := target_distance if occlusion_distance < target_distance: distance = occlusion_distance if abs(distance - position.z) < 0.01: return position.z = lerpf(position.z, distance, zoom_interpolation * delta) func _physics_process(_delta): occlusion_distance = far var corner: Vector2 = get_viewport().get_visible_rect().size var space_state = get_world_3d().direct_space_state send_ray(space_state, Vector2(0, 0)) send_ray(space_state, Vector2(corner.x, 0)) send_ray(space_state, Vector2(0, corner.y)) send_ray(space_state, Vector2(corner.x, corner.y)) send_ray(space_state, Vector2(corner.x / 2, corner.y / 2)) func send_ray(space_state: PhysicsDirectSpaceState3D, pos: Vector2): var from := pivot.global_position var to := project_position(pos, -1) var query = PhysicsRayQueryParameters3D.create(from, to, collision_mask) var result = space_state.intersect_ray(query) if result: var hit_point: Vector3 = result.position var diff := hit_point - pivot.global_position var distance := diff.length() if distance < occlusion_distance: occlusion_distance = distance