GestureArea.gd 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. extends Control
  2. @export var target: NodePath
  3. @export var min_scale := 0.1
  4. @export var max_scale := 3.0
  5. @export var one_finger_rot_x := true
  6. @export var one_finger_rot_y := true
  7. @export var two_fingers_rot_z := true
  8. @export var two_fingers_zoom := true
  9. var base_state := {}
  10. var curr_state := {}
  11. # We keep here a copy of the state before the number of fingers changed to avoid accumulation errors.
  12. var base_xform: Transform3D
  13. @onready var target_node: Node = get_node(target)
  14. func _gui_input(event: InputEvent) -> void:
  15. # We must start touching inside, but we can drag or unpress outside.
  16. # if not (event is InputEventScreenDrag or
  17. # (event is InputEventScreenTouch and (not event.pressed or get_global_rect().has_point(event.position)))):
  18. # return
  19. var finger_count := base_state.size()
  20. if finger_count == 0:
  21. # No fingers => Accept press.
  22. if event is InputEventScreenTouch:
  23. if event.pressed:
  24. # A finger started touching.
  25. base_state = {
  26. event.index: event.position,
  27. }
  28. elif finger_count == 1:
  29. # One finger => For rotating around X and Y.
  30. # Accept one more press, unpress or drag.
  31. if event is InputEventScreenTouch:
  32. if event.pressed:
  33. # One more finger started touching.
  34. # Reset the base state to the only current and the new fingers.
  35. base_state = {
  36. curr_state.keys()[0]: curr_state.values()[0],
  37. event.index: event.position,
  38. }
  39. else:
  40. if base_state.has(event.index):
  41. # Only touching finger released.
  42. base_state.clear()
  43. elif event is InputEventScreenDrag:
  44. if curr_state.has(event.index):
  45. # Touching finger dragged.
  46. var unit_drag := _px2unit(base_state[base_state.keys()[0]] - event.position)
  47. if one_finger_rot_x:
  48. target_node.global_rotate(Vector3.UP, deg_to_rad(180.0 * unit_drag.x))
  49. if one_finger_rot_y:
  50. target_node.global_rotate(Vector3.RIGHT, deg_to_rad(180.0 * unit_drag.y))
  51. # Since rotating around two axes, we have to reset the base constantly.
  52. curr_state[event.index] = event.position
  53. base_state[event.index] = event.position
  54. base_xform = target_node.get_transform()
  55. elif finger_count == 2:
  56. # Two fingers => To pinch-zoom and rotate around Z.
  57. # Accept unpress or drag.
  58. if event is InputEventScreenTouch:
  59. if not event.pressed and base_state.has(event.index):
  60. # Some known touching finger released.
  61. # Clear the base state
  62. base_state.clear()
  63. elif event is InputEventScreenDrag:
  64. if curr_state.has(event.index):
  65. # Some known touching finger dragged.
  66. curr_state[event.index] = event.position
  67. # Compute base and current inter-finger vectors.
  68. var base_segment: Vector3 = base_state[base_state.keys()[0]] - base_state[base_state.keys()[1]]
  69. var new_segment: Vector3 = curr_state[curr_state.keys()[0]] - curr_state[curr_state.keys()[1]]
  70. # Get the base scale from the base matrix.
  71. var base_scale := Vector3(base_xform.basis.x.x, base_xform.basis.y.y, base_xform.basis.z.z).length()
  72. if two_fingers_zoom:
  73. # Compute the new scale limiting it and taking into account the base scale.
  74. var new_scale := clampf(base_scale * (new_segment.length() / base_segment.length()), min_scale, max_scale) / base_scale
  75. target_node.set_transform(base_xform.scaled(new_scale * Vector3.ONE))
  76. else:
  77. target_node.set_transform(base_xform)
  78. if two_fingers_rot_z:
  79. # Apply rotation between base inter-finger vector and the current one.
  80. var rot := new_segment.angle_to(base_segment)
  81. target_node.global_rotate(Vector3.BACK, rot)
  82. # Finger count changed?
  83. if base_state.size() != finger_count:
  84. # Copy new base state to the current state.
  85. curr_state = {}
  86. for idx: int in base_state.keys():
  87. curr_state[idx] = base_state[idx]
  88. # Remember the base transform.
  89. base_xform = target_node.get_transform()
  90. # Converts a vector in pixels to a unitary magnitude,
  91. # considering the number of pixels of the shorter axis is the unit.
  92. func _px2unit(v: Vector2) -> Vector2:
  93. var shortest := minf(get_size().x, get_size().y)
  94. return v * (1.0 / shortest)