GestureArea.gd 4.1 KB

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