pickup_handler.gd 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. @tool
  2. extends Area3D
  3. class_name PickupHandler3D
  4. # This area3D class detects all physics bodys based on
  5. # PickupAbleBody3D within range and handles the logic
  6. # for selecting the closest one and allowing pickup
  7. # of that object.
  8. # Detect range specifies within what radius we detect
  9. # objects we can pick up.
  10. @export var detect_range : float = 0.3:
  11. set(value):
  12. detect_range = value
  13. if is_inside_tree():
  14. _update_detect_range()
  15. _update_closest_body()
  16. # Pickup Action specifies the action in the OpenXR
  17. # action map that triggers our pickup function.
  18. @export var pickup_action : String = "pickup"
  19. var closest_body : PickupAbleBody3D
  20. var picked_up_body: PickupAbleBody3D
  21. var was_pickup_pressed : bool = false
  22. # Update our detection range.
  23. func _update_detect_range() -> void:
  24. var shape : SphereShape3D = $CollisionShape3D.shape
  25. if shape:
  26. shape.radius = detect_range
  27. # Update our closest body.
  28. func _update_closest_body() -> void:
  29. # Do not do this when we're in the editor.
  30. if Engine.is_editor_hint():
  31. return
  32. # Do not check this if we've picked something up.
  33. if picked_up_body:
  34. if closest_body:
  35. closest_body.remove_is_closest(self)
  36. closest_body = null
  37. return
  38. # Find the body that is currently the closest.
  39. var new_closest_body : PickupAbleBody3D
  40. var closest_distance : float = 1000000.0
  41. for body in get_overlapping_bodies():
  42. if body is PickupAbleBody3D and not body.is_picked_up():
  43. var distance_squared = (body.global_position - global_position).length_squared()
  44. if distance_squared < closest_distance:
  45. new_closest_body = body
  46. closest_distance = distance_squared
  47. # Unchanged? Just exit
  48. if closest_body == new_closest_body:
  49. return
  50. # We had a closest body
  51. if closest_body:
  52. closest_body.remove_is_closest(self)
  53. closest_body = new_closest_body
  54. if closest_body:
  55. closest_body.add_is_closest(self)
  56. # Get our controller that we are a child of
  57. func _get_parent_controller() -> XRController3D:
  58. var parent : Node = get_parent()
  59. while parent:
  60. if parent is XRController3D:
  61. return parent
  62. parent = parent.get_parent()
  63. return null
  64. # Called when the node enters the scene tree for the first time.
  65. func _ready() -> void:
  66. _update_detect_range()
  67. _update_closest_body()
  68. # Called every physics frame
  69. func _physics_process(delta) -> void:
  70. # As we move our hands we need to check if the closest body
  71. # has changed.
  72. _update_closest_body()
  73. # Check if our pickup action is true
  74. var pickup_pressed = false
  75. var controller : XRController3D = _get_parent_controller()
  76. if controller:
  77. # While OpenXR can return this as a boolean, there is a lot of
  78. # difference in handling thresholds between platforms.
  79. # So we implement our own logic here.
  80. var pickup_value : float = controller.get_float(pickup_action)
  81. var threshold : float = 0.4 if was_pickup_pressed else 0.6
  82. pickup_pressed = pickup_value > threshold
  83. # Do we need to let go?
  84. if picked_up_body and not pickup_pressed:
  85. picked_up_body.let_go()
  86. picked_up_body = null
  87. # Do we need to pick something up
  88. if not picked_up_body and not was_pickup_pressed and pickup_pressed and closest_body:
  89. picked_up_body = closest_body
  90. picked_up_body.pick_up(self)
  91. # Remember our state for the next frame
  92. was_pickup_pressed = pickup_pressed