class_name Socket extends Node3D @export var snapPoint: Marker3D @export var snapDistance: float = 0.5 @export var lockOnSnap: bool = true var candidate: Pickupable = null var snappedObject: Pickupable = null func is_in_range(object: Pickupable) -> bool: if object == null: return false if object.snapAnchor == null: return false var distance := object.snapAnchor.global_position.distance_to( snapPoint.global_position ) return distance <= snapDistance func getSnapTransform(object: Pickupable) -> Transform3D: var objectTransform := object.body.global_transform var anchorTransform := object.snapAnchor.global_transform # Where is the SnapAnchor relative to the Rigidbody? var anchorOffset := objectTransform.affine_inverse() * anchorTransform # We want the SnapAnchor to have exactly the same # transform as the Socket's SnapPoint. return snapPoint.global_transform * anchorOffset.affine_inverse() func snapObject(object: Pickupable): if not is_in_range(object): return var targetTransform := getSnapTransform(object) var body := object.body body.linear_velocity = Vector3.ZERO body.angular_velocity = Vector3.ZERO body.global_transform = targetTransform if lockOnSnap: body.freeze = true snappedObject = object candidate = null