Advertisement
Guest User

Smoothed Camera

a guest
Jun 11th, 2025
13
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
GDScript 3.70 KB | Source Code | 0 0
  1. #=========================
  2. # DESCRIPTION
  3. #=========================
  4. # Default camera that follows the movement of the player
  5.  
  6. extends Camera2D
  7.  
  8. # Input names
  9. const INPUT_MOVE_LEFT: String = "move_left"
  10. const INPUT_MOVE_RIGHT: String = "move_right"
  11. const INPUT_MOVE_UP: String = "move_up"
  12. const INPUT_MOVE_DOWN: String = "move_down"
  13.  
  14.  
  15. @export_range(0.0, 50.0, 0.001, "or_greater") var look_ahead_distance: float = 20.0
  16. @export_range(0.0, 3.0) var smoothing_duration: float = 1.9
  17.  
  18. ## Curve min value should be 1.0.
  19. ## Max can be modified to suit behavior, but larger value than 15.0 usually causes very quick snapping
  20. @export var position_smoothing_curve: Curve
  21.  
  22. ## Small changes in direction won't slow camera speed too much. Closer to 1.0 means faster snap
  23. @export_range(0.0, 1.0) var small_angle_smooth_ratio: float = 0.37
  24.  
  25. var _player: Player
  26. var _input_vector: Vector2 = Vector2.ZERO
  27. var _last_input_vector: Vector2 = Vector2.ZERO
  28. var _target_position: Vector2 = Vector2.ZERO
  29. var _smoothing_timer: float = 0.0
  30. @onready var _small_angle_duration: float = smoothing_duration * small_angle_smooth_ratio
  31.  
  32.  
  33. func _ready() -> void:
  34.     await owner.ready
  35.     _player = get_tree().get_first_node_in_group(GameConstants.PLAYER_GROUP)
  36.     assert(_player != null, "Player is null, Default Camera will not work")
  37.    
  38.     position = _player.position
  39.     _target_position = _player.position
  40.  
  41.  
  42. func _process(delta: float) -> void:
  43.     if _player == null:
  44.         position = Vector2.ZERO
  45.         return
  46.    
  47.     _input_vector = get_input_normalize()
  48.     if not _input_vector.is_equal_approx(Vector2.ZERO):
  49.         _target_position = calculate_look_ahead_position()
  50.    
  51.     set_smoothing_timer(delta)
  52.    
  53.     var smoothing_curve_domain: float = clampf(_smoothing_timer / smoothing_duration, 0.0 , 1.0)
  54.     var smoothing_weight: float = position_smoothing_curve.sample(smoothing_curve_domain) / 100
  55.    
  56.     position = position.lerp(_target_position, smoothing_weight)
  57.  
  58.  
  59. func calculate_look_ahead_position() -> Vector2:
  60.     return _player.position + change_to_isometric_coordinates(_last_input_vector * look_ahead_distance)
  61.  
  62.  
  63. func get_input_normalize() -> Vector2:
  64.     var input = Input.get_vector(INPUT_MOVE_LEFT, INPUT_MOVE_RIGHT, INPUT_MOVE_UP, INPUT_MOVE_DOWN)
  65.     return input.normalized()
  66.  
  67.  
  68. # NOTE: The smoothing timer is the most important value to determine the behavior of the camera.
  69. # The behavior of the camera can be described as follow:
  70. # 1. If the player releases the input, camera will lerp to the target position based on last directional input
  71. # 2. When player holds down the input, camera will accelerate ahead of the direction player is moving in
  72. # 3. When player changes direction, and if the angle is less than 45 degrees, let the camera moves at a higher
  73. #    speed immediately (but not the max speed).
  74. func set_smoothing_timer(delta: float) -> void:
  75.     if _input_vector.is_equal_approx(Vector2.ZERO):
  76.         if position.is_equal_approx(_target_position):
  77.             _smoothing_timer = 0.0
  78.         else:
  79.             _smoothing_timer += delta
  80.     elif _last_input_vector.is_equal_approx(_input_vector):
  81.             _smoothing_timer += delta
  82.     else:
  83.         if is_angle_less_than_45_degrees(_input_vector.angle_to(_last_input_vector)) \
  84.                 and _smoothing_timer > _small_angle_duration:
  85.             _smoothing_timer = _small_angle_duration
  86.         else:
  87.             _smoothing_timer = 0.0
  88.        
  89.         _last_input_vector = _input_vector
  90.  
  91.  
  92. func change_to_isometric_coordinates(vector: Vector2) -> Vector2:
  93.     return Vector2(vector.x, vector.y / GameConstants.X_TO_Y_RATIO)
  94.  
  95.  
  96. # Performing comparison with angle_to operation of Vector2 can give wrong result bacause of
  97. # computational error margin, which necessitates check against such margins.
  98. func is_angle_less_than_45_degrees(angle: float) -> bool:
  99.     return absf(absf(angle) - (PI / 4)) < 0.01
  100.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement