123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- @tool
- extends Area3D
- class_name PickupHandler3D
- # This area3D class detects all physics bodys based on
- # PickupAbleBody3D within range and handles the logic
- # for selecting the closest one and allowing pickup
- # of that object.
- # Detect range specifies within what radius we detect
- # objects we can pick up.
- @export var detect_range : float = 0.3:
- set(value):
- detect_range = value
- if is_inside_tree():
- _update_detect_range()
- _update_closest_body()
- # Pickup Action specifies the action in the OpenXR
- # action map that triggers our pickup function.
- @export var pickup_action : String = "pickup"
- var closest_body : PickupAbleBody3D
- var picked_up_body: PickupAbleBody3D
- var was_pickup_pressed : bool = false
- # Update our detection range.
- func _update_detect_range() -> void:
- var shape : SphereShape3D = $CollisionShape3D.shape
- if shape:
- shape.radius = detect_range
- # Update our closest body.
- func _update_closest_body() -> void:
- # Do not do this when we're in the editor.
- if Engine.is_editor_hint():
- return
- # Do not check this if we've picked something up.
- if picked_up_body:
- if closest_body:
- closest_body.remove_is_closest(self)
- closest_body = null
- return
- # Find the body that is currently the closest.
- var new_closest_body : PickupAbleBody3D
- var closest_distance : float = 1000000.0
- for body in get_overlapping_bodies():
- if body is PickupAbleBody3D and not body.is_picked_up():
- var distance_squared = (body.global_position - global_position).length_squared()
- if distance_squared < closest_distance:
- new_closest_body = body
- closest_distance = distance_squared
- # Unchanged? Just exit
- if closest_body == new_closest_body:
- return
- # We had a closest body
- if closest_body:
- closest_body.remove_is_closest(self)
- closest_body = new_closest_body
- if closest_body:
- closest_body.add_is_closest(self)
- # Get our controller that we are a child of
- func _get_parent_controller() -> XRController3D:
- var parent : Node = get_parent()
- while parent:
- if parent is XRController3D:
- return parent
- parent = parent.get_parent()
- return null
- # Called when the node enters the scene tree for the first time.
- func _ready() -> void:
- _update_detect_range()
- _update_closest_body()
- # Called every physics frame
- func _physics_process(delta) -> void:
- # As we move our hands we need to check if the closest body
- # has changed.
- _update_closest_body()
- # Check if our pickup action is true
- var pickup_pressed = false
- var controller : XRController3D = _get_parent_controller()
- if controller:
- # While OpenXR can return this as a boolean, there is a lot of
- # difference in handling thresholds between platforms.
- # So we implement our own logic here.
- var pickup_value : float = controller.get_float(pickup_action)
- var threshold : float = 0.4 if was_pickup_pressed else 0.6
- pickup_pressed = pickup_value > threshold
- # Do we need to let go?
- if picked_up_body and not pickup_pressed:
- picked_up_body.let_go()
- picked_up_body = null
- # Do we need to pick something up
- if not picked_up_body and not was_pickup_pressed and pickup_pressed and closest_body:
- picked_up_body = closest_body
- picked_up_body.pick_up(self)
- # Remember our state for the next frame
- was_pickup_pressed = pickup_pressed
|