follow_camera.gd 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. extends Camera3D
  2. # Higher values cause the field of view to increase more at high speeds.
  3. const FOV_SPEED_FACTOR = 60
  4. # Higher values cause the field of view to adapt to speed changes faster.
  5. const FOV_SMOOTH_FACTOR = 0.2
  6. # Don't change FOV if moving below this speed. This prevents shadows from flickering when driving slowly.
  7. const FOV_CHANGE_MIN_SPEED = 0.05
  8. @export var min_distance := 2.0
  9. @export var max_distance := 4.0
  10. @export var angle_v_adjust := 0.0
  11. @export var height := 1.5
  12. var camera_type := CameraType.EXTERIOR
  13. var initial_transform := transform
  14. var base_fov := fov
  15. # The field of view to smoothly interpolate to.
  16. var desired_fov := fov
  17. # Position on the last physics frame (used to measure speed).
  18. var previous_position := global_position
  19. enum CameraType {
  20. EXTERIOR,
  21. INTERIOR,
  22. TOP_DOWN,
  23. MAX, # Represents the size of the CameraType enum.
  24. }
  25. func _ready():
  26. update_camera()
  27. func _input(event):
  28. if event.is_action_pressed(&"cycle_camera"):
  29. camera_type = wrapi(camera_type + 1, 0, CameraType.MAX) as CameraType
  30. update_camera()
  31. func _physics_process(_delta):
  32. if camera_type == CameraType.EXTERIOR:
  33. var target: Vector3 = get_parent().global_transform.origin
  34. var pos := global_transform.origin
  35. var from_target := pos - target
  36. # Check ranges.
  37. if from_target.length() < min_distance:
  38. from_target = from_target.normalized() * min_distance
  39. elif from_target.length() > max_distance:
  40. from_target = from_target.normalized() * max_distance
  41. from_target.y = height
  42. pos = target + from_target
  43. look_at_from_position(pos, target, Vector3.UP)
  44. elif camera_type == CameraType.TOP_DOWN:
  45. position.x = get_parent().global_transform.origin.x
  46. position.z = get_parent().global_transform.origin.z
  47. # Force rotation to prevent camera from being slanted after switching cameras while on a slope.
  48. rotation_degrees = Vector3(270, 180, 0)
  49. # Dynamic field of view based on car speed, with smoothing to prevent sudden changes on impact.
  50. desired_fov = clamp(base_fov + (abs(global_position.length() - previous_position.length()) - FOV_CHANGE_MIN_SPEED) * FOV_SPEED_FACTOR, base_fov, 100)
  51. fov = lerpf(fov, desired_fov, FOV_SMOOTH_FACTOR)
  52. # Turn a little up or down
  53. transform.basis = Basis(transform.basis[0], deg_to_rad(angle_v_adjust)) * transform.basis
  54. previous_position = global_position
  55. func update_camera():
  56. match camera_type:
  57. CameraType.EXTERIOR:
  58. transform = initial_transform
  59. CameraType.INTERIOR:
  60. global_transform = get_node(^"../../InteriorCameraPosition").global_transform
  61. CameraType.TOP_DOWN:
  62. global_transform = get_node(^"../../TopDownCameraPosition").global_transform
  63. # This detaches the camera transform from the parent spatial node, but only
  64. # for exterior and top-down cameras.
  65. set_as_top_level(camera_type != CameraType.INTERIOR)