fix: Updates those camera commands that make use of translation to compensate for drag margins.

This commit is contained in:
Duncan Brown
2022-10-22 19:14:03 -04:00
parent 164d094b63
commit cc74206e6c

View File

@@ -3,6 +3,13 @@ extends Camera2D
class_name ESCCamera
enum Compensation {
NONE,
ADDED,
SUBTRACTED
}
# Reference to the tween node for animating camera movements
var _tween: Tween
@@ -15,6 +22,9 @@ var _follow_target: Node = null
# Target zoom of the camera
var _zoom_target: Vector2
var _global_pos_x_compensation = Compensation.NONE
var _global_pos_y_compensation = Compensation.NONE
# Prepare the tween
func _ready():
@@ -112,8 +122,10 @@ func set_target(p_target, p_time : float = 0.0):
self,
"Current camera position = %s." % str(self.global_position)
)
if p_time == 0.0:
_target = _clamp_to_limits(_target)
_target = _compensate_for_drag_margin_relative(_target, true)
self.global_position = _target
else:
if _tween.is_active():
@@ -126,6 +138,11 @@ func set_target(p_target, p_time : float = 0.0):
)
_tween.emit_signal("tween_completed")
set_drag_margin_enabled(false, false)
_target = _clamp_to_limits(_target)
_convert_current_global_pos_for_drag_margin()
_tween.interpolate_property(
self,
"global_position",
@@ -197,7 +214,10 @@ func push(p_target, p_time: float = 0.0, p_type: int = 0):
push_target = _target
if p_time == 0:
push_target = _clamp_to_limits(push_target)
push_target = _compensate_for_drag_margin_relative(push_target, true)
self.global_position = push_target
if _zoom_target != Vector2():
self.zoom = _zoom_target
else:
@@ -222,6 +242,11 @@ func push(p_target, p_time: float = 0.0, p_type: int = 0):
Tween.EASE_IN_OUT
)
set_drag_margin_enabled(false, false)
push_target = _clamp_to_limits(push_target)
_convert_current_global_pos_for_drag_margin()
_tween.interpolate_property(
self,
"global_position",
@@ -248,8 +273,11 @@ func push(p_target, p_time: float = 0.0, p_type: int = 0):
func shift(p_target: Vector2, p_time: float, p_type: int):
_follow_target = null
set_drag_margin_enabled(false, false)
var new_pos = self.global_position + p_target
_target = new_pos
_target = _clamp_to_limits(new_pos)
_target = _compensate_for_drag_margin_relative(_target)
if _tween.is_active():
escoria.logger.warn(
@@ -261,11 +289,13 @@ func shift(p_target: Vector2, p_time: float, p_type: int):
)
_tween.emit_signal("tween_completed")
_convert_current_global_pos_for_drag_margin()
_tween.interpolate_property(
self,
"global_position",
self.global_position,
new_pos,
_target,
p_time,
p_type,
Tween.EASE_IN_OUT
@@ -275,3 +305,127 @@ func shift(p_target: Vector2, p_time: float, p_type: int):
func _target_reached():
_tween.stop_all()
set_drag_margin_enabled(true, true)
# Ensures that to_clamp doesn't go outside of the pre-set viewing limits.
func _clamp_to_limits(to_clamp: Vector2) -> Vector2:
var clamped_value: Vector2 = to_clamp
var viewport_rect: Rect2 = get_viewport_rect()
if clamped_value.x < limit_left + viewport_rect.size.x * 0.5:
clamped_value.x = limit_left + viewport_rect.size.x * 0.5
elif clamped_value.x > limit_right - viewport_rect.size.x * 0.5:
clamped_value.x = limit_right - viewport_rect.size.x * 0.5
if clamped_value.y < limit_top + viewport_rect.size.y * 0.5:
clamped_value.y = limit_top + viewport_rect.size.y * 0.5
elif clamped_value.y > limit_bottom - viewport_rect.size.y * 0.5:
clamped_value.y = limit_bottom - viewport_rect.size.y * 0.5
return clamped_value
# We have to compensate for rendering calculations Godot does when it renders the camera each frame.
# We do this since drag margins may be enabled and can skew the resulting position as noted above,
# especially given that the anchor mode is DRAG_CENTER.
#
# (See https://github.com/godotengine/godot/blob/3.5/scene/2d/camera_2d.cpp for more details.)
#
# This helps to ensure that when we disable or enable drag margins that the position on the screen
# is maintained without the camera "jumping".
#
# We also note the relative "compensation" if the position is the camera's global_position since
# if we move from drag margins enabled to disabled (or vice versa), we need to know the appropriate
# amount to offset the original compensation. This is due to Godot's own Camera2D calculations when
# rendering the screen/camera (again, see the link above).
#
# This is something of a hack until we decide on whether we implement an Escoria-specific camera
# instead of relying on Camera2D.
func _compensate_for_drag_margin_relative(p_position: Vector2, for_global_pos: bool = false) -> Vector2:
var ret_position: Vector2 = p_position
var viewport_rect: Rect2 = get_viewport_rect()
if drag_margin_h_enabled:
if ret_position.x < self.global_position.x:
ret_position.x = ret_position.x - viewport_rect.size.x * 0.5 * zoom.x * drag_margin_left
if for_global_pos:
_global_pos_x_compensation = Compensation.SUBTRACTED
else:
ret_position.x = ret_position.x + viewport_rect.size.x * 0.5 * zoom.x * drag_margin_right
if for_global_pos:
_global_pos_x_compensation = Compensation.ADDED
else:
if ret_position.x < self.global_position.x:
ret_position.x = ret_position.x + viewport_rect.size.x * 0.5 * zoom.x * drag_margin_left
if for_global_pos:
_global_pos_x_compensation = Compensation.ADDED
else:
ret_position.x = ret_position.x - viewport_rect.size.x * 0.5 * zoom.x * drag_margin_right
if for_global_pos:
_global_pos_x_compensation = Compensation.SUBTRACTED
if drag_margin_v_enabled:
if ret_position.y < self.global_position.y:
ret_position.y = ret_position.y - viewport_rect.size.y * 0.5 * zoom.y * drag_margin_top
if for_global_pos:
_global_pos_y_compensation = Compensation.SUBTRACTED
else:
ret_position.y = ret_position.y + viewport_rect.size.y * 0.5 * zoom.y * drag_margin_bottom
if for_global_pos:
_global_pos_y_compensation = Compensation.ADDED
else:
if ret_position.y < self.global_position.y:
ret_position.y = ret_position.y + viewport_rect.size.y * 0.5 * zoom.y * drag_margin_top
if for_global_pos:
_global_pos_y_compensation = Compensation.ADDED
else:
ret_position.y = ret_position.y - viewport_rect.size.y * 0.5 * zoom.y * drag_margin_bottom
if for_global_pos:
_global_pos_y_compensation = Compensation.SUBTRACTED
return ret_position
# Use this to compensate the camera's current (read: fixed) global_position when enabling/disabling
# drag margins.
func _convert_current_global_pos_for_drag_margin() -> void:
var ret_position: Vector2 = self.global_position
var viewport_rect: Rect2 = get_viewport_rect()
if drag_margin_h_enabled:
if _global_pos_x_compensation == Compensation.ADDED:
ret_position.x = ret_position.x - viewport_rect.size.x * 0.5 * zoom.x * drag_margin_left
elif _global_pos_x_compensation == Compensation.SUBTRACTED:
ret_position.x = ret_position.x + viewport_rect.size.x * 0.5 * zoom.x * drag_margin_right
else:
if _global_pos_x_compensation == Compensation.ADDED:
ret_position.x = ret_position.x - viewport_rect.size.x * 0.5 * zoom.x * drag_margin_right
elif _global_pos_x_compensation == Compensation.SUBTRACTED:
ret_position.x = ret_position.x + viewport_rect.size.x * 0.5 * zoom.x * drag_margin_left
if drag_margin_v_enabled:
if _global_pos_y_compensation == Compensation.ADDED:
ret_position.y = ret_position.y - viewport_rect.size.y * 0.5 * zoom.y * drag_margin_top
elif _global_pos_y_compensation == Compensation.SUBTRACTED:
ret_position.y = ret_position.y + viewport_rect.size.y * 0.5 * zoom.y * drag_margin_bottom
else:
if _global_pos_y_compensation == Compensation.ADDED:
ret_position.y = ret_position.y - viewport_rect.size.y * 0.5 * zoom.y * drag_margin_bottom
elif _global_pos_y_compensation == Compensation.SUBTRACTED:
ret_position.y = ret_position.y + viewport_rect.size.y * 0.5 * zoom.y * drag_margin_top
_global_pos_x_compensation = Compensation.NONE
_global_pos_y_compensation = Compensation.NONE
self.global_position = ret_position