You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			459 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
		
		
			
		
	
	
			459 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
| 
								 
											1 year ago
										 
									 | 
							
								using System.Collections;
							 | 
						||
| 
								 | 
							
								using System.Collections.Generic;
							 | 
						||
| 
								 | 
							
								using UnityEngine;
							 | 
						||
| 
								 | 
							
								using UnityEngine.Events;
							 | 
						||
| 
								 | 
							
								using NaughtyAttributes;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace Autohand {
							 | 
						||
| 
								 | 
							
								    [DefaultExecutionOrder(2)]
							 | 
						||
| 
								 | 
							
								    [HelpURL("https://app.gitbook.com/s/5zKO0EvOjzUDeT2aiFk3/auto-hand/grabbable/distance-grabbing")]
							 | 
						||
| 
								 | 
							
								    public class HandDistanceGrabber : MonoBehaviour {
							 | 
						||
| 
								 | 
							
								        [Header("Hands")]
							 | 
						||
| 
								 | 
							
								        [Tooltip("The primaryHand used to trigger pulling or flicking")]
							 | 
						||
| 
								 | 
							
								        public Hand primaryHand;
							 | 
						||
| 
								 | 
							
								        [Tooltip("This is important for catch assistance")]
							 | 
						||
| 
								 | 
							
								        public Hand secondaryHand;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Header("Pointing Options")]
							 | 
						||
| 
								 | 
							
								        public Transform forwardPointer;
							 | 
						||
| 
								 | 
							
								        public LineRenderer line;
							 | 
						||
| 
								 | 
							
								        [Space]
							 | 
						||
| 
								 | 
							
								        public float maxRange = 5;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Defaults to grabbable on start if none")]
							 | 
						||
| 
								 | 
							
								        public LayerMask layers;
							 | 
						||
| 
								 | 
							
								        [Space]
							 | 
						||
| 
								 | 
							
								        public Material defaultTargetedMaterial;
							 | 
						||
| 
								 | 
							
								        [Tooltip("The highlight material to use when pulling")]
							 | 
						||
| 
								 | 
							
								        public Material defaultSelectedMaterial;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Header("Pull Options")]
							 | 
						||
| 
								 | 
							
								        public bool useInstantPull = false;
							 | 
						||
| 
								 | 
							
								        [Tooltip("If false will default to distance pull, set pullGrabDistance to 0 for instant pull on select")]
							 | 
						||
| 
								 | 
							
								        public bool useFlickPull = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Tooltip("The magnitude of your hands angular velocity for \"flick\" to start")]
							 | 
						||
| 
								 | 
							
								        [ShowIf("useFlickPull")]
							 | 
						||
| 
								 | 
							
								        public float flickThreshold = 7f;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Tooltip("The amount you need to move your hand from the select position to trigger the grab")]
							 | 
						||
| 
								 | 
							
								        [HideIf("useFlickPull")]
							 | 
						||
| 
								 | 
							
								        public float pullGrabDistance = 0.1f;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Space]
							 | 
						||
| 
								 | 
							
								        [Tooltip("If this is true the object will be grabbed when entering the radius")]
							 | 
						||
| 
								 | 
							
								        public bool instantGrabAssist = true;
							 | 
						||
| 
								 | 
							
								        [Tooltip("The radius around of thrown object")]
							 | 
						||
| 
								 | 
							
								        public float catchAssistRadius = 0.2f;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [AutoToggleHeader("Show Events")]
							 | 
						||
| 
								 | 
							
								        public bool showEvents = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [ShowIf("showEvents")]
							 | 
						||
| 
								 | 
							
								        public UnityHandGrabEvent OnPull;
							 | 
						||
| 
								 | 
							
								        [ShowIf("showEvents")]
							 | 
						||
| 
								 | 
							
								        public UnityHandEvent StartPoint;
							 | 
						||
| 
								 | 
							
								        [ShowIf("showEvents")]
							 | 
						||
| 
								 | 
							
								        public UnityHandEvent StopPoint;
							 | 
						||
| 
								 | 
							
								        [ShowIf("showEvents"), Tooltip("Targeting is started when object is highlighted")]
							 | 
						||
| 
								 | 
							
								        public UnityHandGrabEvent StartTarget;
							 | 
						||
| 
								 | 
							
								        [ShowIf("showEvents")]
							 | 
						||
| 
								 | 
							
								        public UnityHandGrabEvent StopTarget;
							 | 
						||
| 
								 | 
							
								        [ Tooltip("Selecting is started when grab is selected on highlight object")]
							 | 
						||
| 
								 | 
							
								        [ShowIf("showEvents")]
							 | 
						||
| 
								 | 
							
								        public UnityHandGrabEvent StartSelect;
							 | 
						||
| 
								 | 
							
								        [ShowIf("showEvents")]
							 | 
						||
| 
								 | 
							
								        public UnityHandGrabEvent StopSelect;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        List<CatchAssistData> catchAssisted;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        DistanceGrabbable targetingDistanceGrabbable;
							 | 
						||
| 
								 | 
							
								        DistanceGrabbable selectingDistanceGrabbable;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        float catchAssistSeconds = 3f;
							 | 
						||
| 
								 | 
							
								        bool pointing;
							 | 
						||
| 
								 | 
							
								        bool pulling;
							 | 
						||
| 
								 | 
							
								        Vector3 startPullPosition;
							 | 
						||
| 
								 | 
							
								        RaycastHit hit;
							 | 
						||
| 
								 | 
							
								        Quaternion lastRotation;
							 | 
						||
| 
								 | 
							
								        private RaycastHit selectionHit;
							 | 
						||
| 
								 | 
							
								        float selectedEstimatedRadius;
							 | 
						||
| 
								 | 
							
								        float startLookAssist;
							 | 
						||
| 
								 | 
							
								        bool lastInstantPull;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        GameObject _hitPoint;
							 | 
						||
| 
								 | 
							
								        Coroutine catchAssistRoutine;
							 | 
						||
| 
								 | 
							
								        private DistanceGrabbable catchAsistGrabbable;
							 | 
						||
| 
								 | 
							
								        private CatchAssistData catchAssistData;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        GameObject hitPoint {
							 | 
						||
| 
								 | 
							
								            get {
							 | 
						||
| 
								 | 
							
								                if(!gameObject.activeInHierarchy)
							 | 
						||
| 
								 | 
							
								                    return null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if(_hitPoint == null) {
							 | 
						||
| 
								 | 
							
								                    _hitPoint = new GameObject();
							 | 
						||
| 
								 | 
							
								                    _hitPoint.name = "Distance Hit Point";
							 | 
						||
| 
								 | 
							
								                    return _hitPoint;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                return _hitPoint;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void Start() {
							 | 
						||
| 
								 | 
							
								            catchAssisted = new List<CatchAssistData>();
							 | 
						||
| 
								 | 
							
								            if(layers == 0)
							 | 
						||
| 
								 | 
							
								                layers = LayerMask.GetMask(Hand.grabbableLayerNameDefault);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if(useInstantPull)
							 | 
						||
| 
								 | 
							
								                SetInstantPull();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        private void OnEnable() {
							 | 
						||
| 
								 | 
							
								            primaryHand.OnTriggerGrab += TryCatchAssist;
							 | 
						||
| 
								 | 
							
								            if(secondaryHand != null)
							 | 
						||
| 
								 | 
							
								                secondaryHand.OnTriggerGrab += TryCatchAssist;
							 | 
						||
| 
								 | 
							
								            primaryHand.OnBeforeGrabbed += (hand, grabbable) => { StopPointing(); CancelSelect(); };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        private void OnDisable() {
							 | 
						||
| 
								 | 
							
								            primaryHand.OnTriggerGrab -= TryCatchAssist;
							 | 
						||
| 
								 | 
							
								            if(secondaryHand != null)
							 | 
						||
| 
								 | 
							
								                secondaryHand.OnTriggerGrab -= TryCatchAssist;
							 | 
						||
| 
								 | 
							
								            primaryHand.OnBeforeGrabbed -= (hand, grabbable) => { StopPointing(); CancelSelect(); };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if(catchAssistRoutine != null) {
							 | 
						||
| 
								 | 
							
								                StopCoroutine(catchAssistRoutine);
							 | 
						||
| 
								 | 
							
								                catchAssistRoutine = null;
							 | 
						||
| 
								 | 
							
								                catchAsistGrabbable.grabbable.OnGrabEvent -= (hand, grabbable) => { if(catchAssisted.Contains(catchAssistData)) catchAssisted.Remove(catchAssistData); };
							 | 
						||
| 
								 | 
							
								                catchAsistGrabbable.OnPullCanceled -= (hand, grabbable) => { if(catchAssisted.Contains(catchAssistData)) catchAssisted.Remove(catchAssistData); };
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void Update() {
							 | 
						||
| 
								 | 
							
								            CheckDistanceGrabbable();
							 | 
						||
| 
								 | 
							
								            if(lastInstantPull != useInstantPull) {
							 | 
						||
| 
								 | 
							
								                if(useInstantPull) {
							 | 
						||
| 
								 | 
							
								                    useFlickPull = false;
							 | 
						||
| 
								 | 
							
								                    pullGrabDistance = 0;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                lastInstantPull = useInstantPull;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        private void OnDestroy() {
							 | 
						||
| 
								 | 
							
								            Destroy(hitPoint);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        public void SetInstantPull() {
							 | 
						||
| 
								 | 
							
								            useInstantPull = true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public void SetPull(float distance) {
							 | 
						||
| 
								 | 
							
								            useInstantPull = false;
							 | 
						||
| 
								 | 
							
								            useFlickPull = false;
							 | 
						||
| 
								 | 
							
								            pullGrabDistance = distance;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public void SetFlickPull(float threshold) {
							 | 
						||
| 
								 | 
							
								            useInstantPull = false;
							 | 
						||
| 
								 | 
							
								            useFlickPull = true;
							 | 
						||
| 
								 | 
							
								            flickThreshold = threshold;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void CheckDistanceGrabbable() {
							 | 
						||
| 
								 | 
							
								            if(!pulling && pointing && primaryHand.holdingObj == null) {
							 | 
						||
| 
								 | 
							
								                bool didHit = Physics.SphereCast(forwardPointer.position, 0.03f, forwardPointer.forward, out hit, maxRange, layers);
							 | 
						||
| 
								 | 
							
								                DistanceGrabbable hitGrabbable;
							 | 
						||
| 
								 | 
							
								                GrabbableChild hitGrabbableChild;
							 | 
						||
| 
								 | 
							
								                if(didHit) {
							 | 
						||
| 
								 | 
							
								                    if(hit.transform.CanGetComponent(out hitGrabbable)) {
							 | 
						||
| 
								 | 
							
								                        if(targetingDistanceGrabbable == null || hitGrabbable.GetInstanceID() != targetingDistanceGrabbable.GetInstanceID())
							 | 
						||
| 
								 | 
							
								                        {
							 | 
						||
| 
								 | 
							
								                            StartTargeting(hitGrabbable);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    else if(hit.transform.CanGetComponent(out hitGrabbableChild)) {
							 | 
						||
| 
								 | 
							
								                        if(hitGrabbableChild.grabParent.transform.CanGetComponent(out hitGrabbable)) {
							 | 
						||
| 
								 | 
							
								                            if(targetingDistanceGrabbable == null || hitGrabbable.GetInstanceID() != targetingDistanceGrabbable.GetInstanceID())
							 | 
						||
| 
								 | 
							
								                            {
							 | 
						||
| 
								 | 
							
								                                StartTargeting(hitGrabbable);
							 | 
						||
| 
								 | 
							
								                            }
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    else if(targetingDistanceGrabbable != null && hit.transform.gameObject.GetInstanceID() != targetingDistanceGrabbable.gameObject.GetInstanceID())
							 | 
						||
| 
								 | 
							
								                    {
							 | 
						||
| 
								 | 
							
								                        StopTargeting();
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else {
							 | 
						||
| 
								 | 
							
								                    StopTargeting();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if(line != null) {
							 | 
						||
| 
								 | 
							
								                    if(didHit) {
							 | 
						||
| 
								 | 
							
								                        line.positionCount = 2;
							 | 
						||
| 
								 | 
							
								                        line.SetPositions(new Vector3[] { forwardPointer.position, hit.point });
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    else {
							 | 
						||
| 
								 | 
							
								                        line.positionCount = 2;
							 | 
						||
| 
								 | 
							
								                        line.SetPositions(new Vector3[] { forwardPointer.position, forwardPointer.position + forwardPointer.forward * maxRange });
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if(pulling && primaryHand.holdingObj == null) {
							 | 
						||
| 
								 | 
							
								                if(useFlickPull) {
							 | 
						||
| 
								 | 
							
								                    TryFlickPull();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else {
							 | 
						||
| 
								 | 
							
								                    TryDistancePull();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if(targetingDistanceGrabbable != null) {
							 | 
						||
| 
								 | 
							
								                StopTargeting();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public virtual void StartPointing() {
							 | 
						||
| 
								 | 
							
								            pointing = true;
							 | 
						||
| 
								 | 
							
								            StartPoint?.Invoke(primaryHand);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public virtual void StopPointing() {
							 | 
						||
| 
								 | 
							
								            pointing = false;
							 | 
						||
| 
								 | 
							
								            if(line != null) {
							 | 
						||
| 
								 | 
							
								                line.positionCount = 0;
							 | 
						||
| 
								 | 
							
								                line.SetPositions(new Vector3[0]);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            StopPoint?.Invoke(primaryHand);
							 | 
						||
| 
								 | 
							
								            StopTargeting();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public virtual void StartTargeting(DistanceGrabbable target) {
							 | 
						||
| 
								 | 
							
								            if(target.enabled && primaryHand.CanGrab(target.grabbable)) {
							 | 
						||
| 
								 | 
							
								                if(targetingDistanceGrabbable != null)
							 | 
						||
| 
								 | 
							
								                    StopTargeting();
							 | 
						||
| 
								 | 
							
								                targetingDistanceGrabbable = target;
							 | 
						||
| 
								 | 
							
								                targetingDistanceGrabbable?.grabbable.Highlight(primaryHand, GetTargetedMaterial(targetingDistanceGrabbable));
							 | 
						||
| 
								 | 
							
								                targetingDistanceGrabbable?.StartTargeting?.Invoke(primaryHand, target.grabbable);
							 | 
						||
| 
								 | 
							
								                StartTarget?.Invoke(primaryHand, target.grabbable);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public virtual void StopTargeting() {
							 | 
						||
| 
								 | 
							
								            targetingDistanceGrabbable?.grabbable.Unhighlight(primaryHand, GetTargetedMaterial(targetingDistanceGrabbable));
							 | 
						||
| 
								 | 
							
								            targetingDistanceGrabbable?.StopTargeting?.Invoke(primaryHand, targetingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								            if(targetingDistanceGrabbable != null)
							 | 
						||
| 
								 | 
							
								                StopTarget?.Invoke(primaryHand, targetingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								            else if(selectingDistanceGrabbable != null)
							 | 
						||
| 
								 | 
							
								                StopTarget?.Invoke(primaryHand, selectingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								            targetingDistanceGrabbable = null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public virtual void SelectTarget() {
							 | 
						||
| 
								 | 
							
								            if(targetingDistanceGrabbable != null) {
							 | 
						||
| 
								 | 
							
								                pulling = true;
							 | 
						||
| 
								 | 
							
								                startPullPosition = primaryHand.transform.localPosition;
							 | 
						||
| 
								 | 
							
								                lastRotation = transform.rotation;
							 | 
						||
| 
								 | 
							
								                selectionHit = hit;
							 | 
						||
| 
								 | 
							
								                if(catchAssistRoutine == null) {
							 | 
						||
| 
								 | 
							
								                    hitPoint.transform.position = selectionHit.point;
							 | 
						||
| 
								 | 
							
								                    hitPoint.transform.parent = selectionHit.transform;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                selectingDistanceGrabbable = targetingDistanceGrabbable;
							 | 
						||
| 
								 | 
							
								                selectedEstimatedRadius = Vector3.Distance(hitPoint.transform.position, selectingDistanceGrabbable.grabbable.body.transform.position);
							 | 
						||
| 
								 | 
							
								                selectingDistanceGrabbable.grabbable.Unhighlight(primaryHand, GetTargetedMaterial(selectingDistanceGrabbable));
							 | 
						||
| 
								 | 
							
								                selectingDistanceGrabbable.grabbable.Highlight(primaryHand, GetSelectedMaterial(selectingDistanceGrabbable));
							 | 
						||
| 
								 | 
							
								                selectingDistanceGrabbable?.StartSelecting?.Invoke(primaryHand, selectingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								                targetingDistanceGrabbable?.StopTargeting?.Invoke(primaryHand, selectingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								                targetingDistanceGrabbable = null;
							 | 
						||
| 
								 | 
							
								                StartSelect?.Invoke(primaryHand, selectingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								                StopPointing();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public virtual void CancelSelect() {
							 | 
						||
| 
								 | 
							
								            StopTargeting();
							 | 
						||
| 
								 | 
							
								            pulling = false;
							 | 
						||
| 
								 | 
							
								            selectingDistanceGrabbable?.grabbable.Unhighlight(primaryHand, GetSelectedMaterial(selectingDistanceGrabbable));
							 | 
						||
| 
								 | 
							
								            selectingDistanceGrabbable?.StopSelecting?.Invoke(primaryHand, selectingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								            if(selectingDistanceGrabbable != null)
							 | 
						||
| 
								 | 
							
								                StopSelect?.Invoke(primaryHand, selectingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								            selectingDistanceGrabbable = null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public virtual void ActivatePull() {
							 | 
						||
| 
								 | 
							
								            if(selectingDistanceGrabbable) {
							 | 
						||
| 
								 | 
							
								                OnPull?.Invoke(primaryHand, selectingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								                selectingDistanceGrabbable.OnPull?.Invoke(primaryHand, selectingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								                if(selectingDistanceGrabbable.instantPull) {
							 | 
						||
| 
								 | 
							
								                    selectingDistanceGrabbable.grabbable.body.velocity = Vector3.zero;
							 | 
						||
| 
								 | 
							
								                    selectingDistanceGrabbable.grabbable.body.angularVelocity = Vector3.zero;
							 | 
						||
| 
								 | 
							
								                    selectionHit.point = hitPoint.transform.position;
							 | 
						||
| 
								 | 
							
								                    if (selectingDistanceGrabbable.grabbable.placePoint != null)
							 | 
						||
| 
								 | 
							
								                        selectingDistanceGrabbable.grabbable.placePoint.Remove();
							 | 
						||
| 
								 | 
							
								                    primaryHand.Grab(selectionHit, selectingDistanceGrabbable.grabbable);
							 | 
						||
| 
								 | 
							
								                    CancelSelect();
							 | 
						||
| 
								 | 
							
								                    selectingDistanceGrabbable?.CancelTarget();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else if(selectingDistanceGrabbable.grabType == DistanceGrabType.Velocity) {
							 | 
						||
| 
								 | 
							
								                    catchAssistRoutine = StartCoroutine(StartCatchAssist(selectingDistanceGrabbable, selectedEstimatedRadius));
							 | 
						||
| 
								 | 
							
								                    catchAsistGrabbable = selectingDistanceGrabbable;
							 | 
						||
| 
								 | 
							
								                    if (selectingDistanceGrabbable.grabbable.placePoint != null)
							 | 
						||
| 
								 | 
							
								                    {
							 | 
						||
| 
								 | 
							
								                        
							 | 
						||
| 
								 | 
							
								                        selectingDistanceGrabbable.grabbable.placePoint.Remove();
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    selectingDistanceGrabbable.SetTarget(primaryHand.palmTransform);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else if(selectingDistanceGrabbable.grabType == DistanceGrabType.Linear) {
							 | 
						||
| 
								 | 
							
								                    selectingDistanceGrabbable.grabbable.body.velocity = Vector3.zero;
							 | 
						||
| 
								 | 
							
								                    selectingDistanceGrabbable.grabbable.body.angularVelocity = Vector3.zero;
							 | 
						||
| 
								 | 
							
								                    selectionHit.point = hitPoint.transform.position;
							 | 
						||
| 
								 | 
							
								                    if (selectingDistanceGrabbable.grabbable.placePoint != null)
							 | 
						||
| 
								 | 
							
								                        selectingDistanceGrabbable.grabbable.placePoint.Remove();
							 | 
						||
| 
								 | 
							
								                    primaryHand.Grab(selectionHit, selectingDistanceGrabbable.grabbable, GrabType.GrabbableToHand);
							 | 
						||
| 
								 | 
							
								                    CancelSelect();
							 | 
						||
| 
								 | 
							
								                    selectingDistanceGrabbable?.CancelTarget();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    CancelSelect();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void TryDistancePull() {
							 | 
						||
| 
								 | 
							
								            if(Vector3.Distance(startPullPosition, primaryHand.transform.localPosition) > pullGrabDistance) {
							 | 
						||
| 
								 | 
							
								                ActivatePull();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void TryFlickPull() {
							 | 
						||
| 
								 | 
							
								            Quaternion deltaRotation = transform.rotation * Quaternion.Inverse(lastRotation);
							 | 
						||
| 
								 | 
							
								            lastRotation = transform.rotation;
							 | 
						||
| 
								 | 
							
								            var getAngle = 0f;
							 | 
						||
| 
								 | 
							
								            Vector3 getAxis = Vector3.zero;
							 | 
						||
| 
								 | 
							
								            deltaRotation.ToAngleAxis(out getAngle, out getAxis);
							 | 
						||
| 
								 | 
							
								            getAngle *= Mathf.Deg2Rad;
							 | 
						||
| 
								 | 
							
								            float speed = (getAxis * getAngle * (1f / Time.deltaTime)).magnitude;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if(speed > flickThreshold || useInstantPull) {
							 | 
						||
| 
								 | 
							
								                if(selectingDistanceGrabbable) {
							 | 
						||
| 
								 | 
							
								                    ActivatePull();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Material GetSelectedMaterial(DistanceGrabbable grabbable) {
							 | 
						||
| 
								 | 
							
								            if(grabbable.ignoreHighlights)
							 | 
						||
| 
								 | 
							
								                return null;
							 | 
						||
| 
								 | 
							
								            return grabbable.selectedMaterial != null ? grabbable.selectedMaterial : defaultSelectedMaterial;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        Material GetTargetedMaterial(DistanceGrabbable grabbable) {
							 | 
						||
| 
								 | 
							
								            if(grabbable.ignoreHighlights)
							 | 
						||
| 
								 | 
							
								                return null;
							 | 
						||
| 
								 | 
							
								            return grabbable.selectedMaterial != null ? grabbable.targetedMaterial : defaultTargetedMaterial;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void TryCatchAssist(Hand hand, Grabbable grab) {
							 | 
						||
| 
								 | 
							
								            for(int i = 0; i < catchAssisted.Count; i++) {
							 | 
						||
| 
								 | 
							
								                var distance = Vector3.Distance(hand.palmTransform.position + hand.palmTransform.forward * catchAssistRadius, catchAssisted[i].grab.transform.position) - catchAssisted[i].estimatedRadius;
							 | 
						||
| 
								 | 
							
								                if(distance < catchAssistRadius) {
							 | 
						||
| 
								 | 
							
								                    Ray ray = new Ray(hand.palmTransform.position, hitPoint.transform.position - hand.palmTransform.position);
							 | 
						||
| 
								 | 
							
								                    if(Physics.SphereCast(ray, 0.03f, out var catchHit, catchAssistRadius * 2, LayerMask.GetMask(Hand.grabbableLayerNameDefault, Hand.grabbingLayerName))) {
							 | 
						||
| 
								 | 
							
								                        if(catchHit.transform.gameObject == catchAssisted[i].grab.gameObject) {
							 | 
						||
| 
								 | 
							
								                            catchAssisted[i].grab.body.velocity = Vector3.zero;
							 | 
						||
| 
								 | 
							
								                            catchAssisted[i].grab.body.angularVelocity = Vector3.zero;
							 | 
						||
| 
								 | 
							
								                            hand.Grab(catchHit, catchAssisted[i].grab);
							 | 
						||
| 
								 | 
							
								                            CancelSelect();
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        IEnumerator StartCatchAssist(DistanceGrabbable grab, float estimatedRadius) {
							 | 
						||
| 
								 | 
							
								            catchAssistData = new CatchAssistData(grab.grabbable, catchAssistRadius);
							 | 
						||
| 
								 | 
							
								            catchAssisted.Add(catchAssistData);
							 | 
						||
| 
								 | 
							
								            grab.grabbable.OnGrabEvent += (hand, grabbable) => { if(catchAssisted.Contains(catchAssistData)) catchAssisted.Remove(catchAssistData); };
							 | 
						||
| 
								 | 
							
								            grab.OnPullCanceled += (hand, grabbable) => { if(catchAssisted.Contains(catchAssistData)) catchAssisted.Remove(catchAssistData); };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if(instantGrabAssist) {
							 | 
						||
| 
								 | 
							
								                bool cancelInstantGrab = false;
							 | 
						||
| 
								 | 
							
								                var time = 0f;
							 | 
						||
| 
								 | 
							
								                primaryHand.OnTriggerRelease += (hand, grabbable) => { cancelInstantGrab = true; };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                while(time < catchAssistSeconds && !cancelInstantGrab) {
							 | 
						||
| 
								 | 
							
								                    time += Time.fixedDeltaTime;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if(TryCatch(primaryHand))
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    bool TryCatch(Hand hand) {
							 | 
						||
| 
								 | 
							
								                        var distance = Vector3.Distance(hand.palmTransform.position + hand.palmTransform.forward * catchAssistRadius, grab.transform.position) - estimatedRadius;
							 | 
						||
| 
								 | 
							
								                        if(distance < catchAssistRadius) {
							 | 
						||
| 
								 | 
							
								                            Ray ray = new Ray(hand.palmTransform.position, hitPoint.transform.position - hand.palmTransform.position);
							 | 
						||
| 
								 | 
							
								                            var hits = Physics.SphereCastAll(ray, 0.03f, catchAssistRadius * 2, LayerMask.GetMask(Hand.grabbableLayerNameDefault, Hand.grabbingLayerName));
							 | 
						||
| 
								 | 
							
								                            for(int i = 0; i < hits.Length; i++) {
							 | 
						||
| 
								 | 
							
								                                if(hits[i].transform.gameObject == grab.gameObject) {
							 | 
						||
| 
								 | 
							
								                                    grab.grabbable.body.velocity = Vector3.zero;
							 | 
						||
| 
								 | 
							
								                                    grab.grabbable.body.angularVelocity = Vector3.zero;
							 | 
						||
| 
								 | 
							
								                                    hand.Grab(hits[i], grab.grabbable);
							 | 
						||
| 
								 | 
							
								                                    grab.CancelTarget();
							 | 
						||
| 
								 | 
							
								                                    CancelSelect();
							 | 
						||
| 
								 | 
							
								                                    return true;
							 | 
						||
| 
								 | 
							
								                                }
							 | 
						||
| 
								 | 
							
								                            }
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        return false;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    yield return new WaitForEndOfFrame();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                primaryHand.OnTriggerRelease -= (hand, grabbable) => { cancelInstantGrab = true; };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            else
							 | 
						||
| 
								 | 
							
								                yield return new WaitForSeconds(catchAssistSeconds);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            grab.grabbable.OnGrabEvent -= (hand, grabbable) => { if(catchAssisted.Contains(catchAssistData)) catchAssisted.Remove(catchAssistData); };
							 | 
						||
| 
								 | 
							
								            grab.OnPullCanceled -= (hand, grabbable) => { if(catchAssisted.Contains(catchAssistData)) catchAssisted.Remove(catchAssistData); };
							 | 
						||
| 
								 | 
							
								            if(catchAssisted.Contains(catchAssistData))
							 | 
						||
| 
								 | 
							
								                catchAssisted.Remove(catchAssistData);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            catchAssistRoutine = null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        private void OnDrawGizmosSelected() {
							 | 
						||
| 
								 | 
							
								            if(primaryHand)
							 | 
						||
| 
								 | 
							
								                Gizmos.DrawWireSphere(primaryHand.palmTransform.position + primaryHand.palmTransform.forward * catchAssistRadius * 4 / 5f + primaryHand.palmTransform.up * catchAssistRadius * 1 / 4f, catchAssistRadius);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    struct CatchAssistData {
							 | 
						||
| 
								 | 
							
								        public Grabbable grab;
							 | 
						||
| 
								 | 
							
								        public float estimatedRadius;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public CatchAssistData(Grabbable grab, float estimatedRadius) {
							 | 
						||
| 
								 | 
							
								            this.grab = grab;
							 | 
						||
| 
								 | 
							
								            this.estimatedRadius = estimatedRadius;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |