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.
		
		
		
		
		
			
		
			
	
	
		
			235 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
		
		
			
		
	
	
			235 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
| 
								 
											1 year ago
										 
									 | 
							
								using System.Collections;
							 | 
						||
| 
								 | 
							
								using System.Collections.Generic;
							 | 
						||
| 
								 | 
							
								using UnityEngine;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace Autohand {
							 | 
						||
| 
								 | 
							
								    public class HandFingerTouch
							 | 
						||
| 
								 | 
							
								        : MonoBehaviour {
							 | 
						||
| 
								 | 
							
								        [Tooltip("Reference to the Hand component")]
							 | 
						||
| 
								 | 
							
								        public Hand hand;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Tooltip("Reference to the Index Finger component")]
							 | 
						||
| 
								 | 
							
								        public Finger index;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Reference to the Middle Finger component")]
							 | 
						||
| 
								 | 
							
								        public Finger middle;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Reference to the Ring Finger component")]
							 | 
						||
| 
								 | 
							
								        public Finger ring;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Reference to the Pinky Finger component")]
							 | 
						||
| 
								 | 
							
								        public Finger pinky;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Reference to the Thumb component")]
							 | 
						||
| 
								 | 
							
								        public Finger thumb;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Tooltip("Layer mask for determining what objects the fingers can interact with")]
							 | 
						||
| 
								 | 
							
								        public LayerMask touchMask;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Tooltip("Speed at which finger offset adjusts - These default settings are tuned for the default hands physics settings and results might vary based on hands set follow speed and rigidbody settings\"")]
							 | 
						||
| 
								 | 
							
								        public float offsetSpeed = 0.65f;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Tooltip("Maximum offset for the index finger")]
							 | 
						||
| 
								 | 
							
								        public float maxIndexOffset = 0.5f;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Maximum offset for the middle finger")]
							 | 
						||
| 
								 | 
							
								        public float maxMiddleOffset = 0.5f;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Maximum offset for the ring finger")]
							 | 
						||
| 
								 | 
							
								        public float maxRingOffset = 0.5f;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Maximum offset for the pinky finger")]
							 | 
						||
| 
								 | 
							
								        public float maxPinkyOffset = 0.5f;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Maximum offset for the thumb")]
							 | 
						||
| 
								 | 
							
								        public float maxThumbOffset = 0.5f;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Tooltip("Minimum pressure required for interaction - These default settings are tuned for the default hands physics settings and results might vary based on hands set follow speed and rigidbody settings")]
							 | 
						||
| 
								 | 
							
								        public float minPressure = 300;
							 | 
						||
| 
								 | 
							
								        [Tooltip("Maximum pressure for interaction - These default settings are tuned for the default hands physics settings and results might vary based on hands set follow speed and rigidbody settings")]
							 | 
						||
| 
								 | 
							
								        public float maxPressure = 1200;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        [Tooltip("Curve to represent the relationship between pressure applied and finger bend")]
							 | 
						||
| 
								 | 
							
								        public AnimationCurve pressureBendCurve = AnimationCurve.Linear(0, 0, 1, 1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Internal fields for handling finger touch and pressure
							 | 
						||
| 
								 | 
							
								        private float fingerRadiusMultiplier = 4f;
							 | 
						||
| 
								 | 
							
								        private float[] maxOffsets;
							 | 
						||
| 
								 | 
							
								        private float[] currentPressure;
							 | 
						||
| 
								 | 
							
								        private bool[] isTouching;
							 | 
						||
| 
								 | 
							
								        private Vector3 largeSphereCheckerPoint;
							 | 
						||
| 
								 | 
							
								        private float largeSphereCheckerRadius;
							 | 
						||
| 
								 | 
							
								        private Collider[] collidersNonAlloc = new Collider[100];
							 | 
						||
| 
								 | 
							
								        private Vector3[] fingerTips = new Vector3[5];
							 | 
						||
| 
								 | 
							
								        private Finger[] fingers = new Finger[5];
							 | 
						||
| 
								 | 
							
								        private Vector3 smoothAngularVelocity;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /// <summary>
							 | 
						||
| 
								 | 
							
								        /// Returns a value between 0 and 1 representing the current pressure applied to the finger
							 | 
						||
| 
								 | 
							
								        /// </summary>
							 | 
						||
| 
								 | 
							
								        public float GetCurrentFingerPressure(FingerEnum finger) {
							 | 
						||
| 
								 | 
							
								            return currentPressure[(int)finger];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void Awake() {
							 | 
						||
| 
								 | 
							
								            fingerTips[0] = (hand.transform.InverseTransformPoint(index.tip.position));
							 | 
						||
| 
								 | 
							
								            fingerTips[1] =(hand.transform.InverseTransformPoint(middle.tip.position));
							 | 
						||
| 
								 | 
							
								            fingerTips[2] =(hand.transform.InverseTransformPoint(ring.tip.position));
							 | 
						||
| 
								 | 
							
								            fingerTips[3] =(hand.transform.InverseTransformPoint(pinky.tip.position));
							 | 
						||
| 
								 | 
							
								            fingerTips[4] =(hand.transform.InverseTransformPoint(thumb.tip.position));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            fingers[0] = (index);
							 | 
						||
| 
								 | 
							
								            fingers[1] =(middle);
							 | 
						||
| 
								 | 
							
								            fingers[2] =(ring);
							 | 
						||
| 
								 | 
							
								            fingers[3] =(pinky);
							 | 
						||
| 
								 | 
							
								            fingers[4] =(thumb);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            maxOffsets = new float[] { maxIndexOffset, maxMiddleOffset, maxRingOffset, maxPinkyOffset, maxThumbOffset };
							 | 
						||
| 
								 | 
							
								            currentPressure = new float[] { 0, 0, 0, 0, 0 };
							 | 
						||
| 
								 | 
							
								            isTouching = new bool[] { false, false, false, false, false };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            CreateEncapsulatingSphere(fingerTips, out largeSphereCheckerPoint, out largeSphereCheckerRadius);
							 | 
						||
| 
								 | 
							
								            largeSphereCheckerRadius += index.tipRadius;
							 | 
						||
| 
								 | 
							
								            largeSphereCheckerRadius += middle.tipRadius;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void OnEnable() {
							 | 
						||
| 
								 | 
							
								            StartCoroutine(SlowUpdateCoroutine());
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void OnDisable() {
							 | 
						||
| 
								 | 
							
								            StopAllCoroutines();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void LateUpdate() {
							 | 
						||
| 
								 | 
							
								            MoveTowardsBendTarget();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        IEnumerator SlowUpdateCoroutine() {
							 | 
						||
| 
								 | 
							
								            while (true) {
							 | 
						||
| 
								 | 
							
								                CheckFingerTouch();
							 | 
						||
| 
								 | 
							
								                yield return new WaitForFixedUpdate();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void MoveTowardsBendTarget() {
							 | 
						||
| 
								 | 
							
								            for(int i = 0; i < fingers.Length; i++) {
							 | 
						||
| 
								 | 
							
								                if(fingers[i].secondaryOffset == 0 && currentPressure[i] == 0)
							 | 
						||
| 
								 | 
							
								                    continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                float currentTarget = currentPressure[i]*maxOffsets[i];
							 | 
						||
| 
								 | 
							
								                float targetDistance = Mathf.Abs(currentTarget - fingers[i].secondaryOffset)/maxOffsets[i];
							 | 
						||
| 
								 | 
							
								                targetDistance = Mathf.Pow(targetDistance, 2f);
							 | 
						||
| 
								 | 
							
								                float distanceSpeed = Time.deltaTime * offsetSpeed * targetDistance;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                currentTarget = Mathf.Clamp(currentTarget, -maxOffsets[i], maxOffsets[i]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                fingers[i].secondaryOffset = Mathf.MoveTowards(fingers[i].secondaryOffset, currentTarget, distanceSpeed + Time.deltaTime * offsetSpeed/2f);
							 | 
						||
| 
								 | 
							
								                if(fingers[i].GetCurrentBend() < 0) {
							 | 
						||
| 
								 | 
							
								                    fingers[i].secondaryOffset += -fingers[i].GetCurrentBend();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void CheckFingerTouch() {
							 | 
						||
| 
								 | 
							
								            //var angularVelocity = CalculateCustomAngularVelocity(hand.transform.rotation, hand.moveTo.transform.rotation, 2000f);
							 | 
						||
| 
								 | 
							
								            var targetAngularVelocity = !hand.holdingObj ? hand.lastAngularVelocity : Vector3.zero;
							 | 
						||
| 
								 | 
							
								            //var lerpDelta = Vector3.SqrMagnitude(targetAngularVelocity - smoothAngularVelocity) * Time.fixedDeltaTime * 1200f + Time.fixedDeltaTime * 360f;
							 | 
						||
| 
								 | 
							
								            var lerpDelta =  Time.fixedDeltaTime * 1200f;
							 | 
						||
| 
								 | 
							
								            //smoothAngularVelocity = Vector3.Lerp(smoothAngularVelocity, targetAngularVelocity, lerpDelta);
							 | 
						||
| 
								 | 
							
								            smoothAngularVelocity = targetAngularVelocity;//Vector3.MoveTowards(smoothAngularVelocity, targetAngularVelocity, lerpDelta);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            //Check for overlap with large sphere
							 | 
						||
| 
								 | 
							
								            int numColliders = Physics.OverlapSphereNonAlloc(hand.transform.TransformPoint(largeSphereCheckerPoint), largeSphereCheckerRadius, collidersNonAlloc, touchMask, QueryTriggerInteraction.Ignore);
							 | 
						||
| 
								 | 
							
								            if (numColliders > 0) {
							 | 
						||
| 
								 | 
							
								                //Check for overlap with each finger
							 | 
						||
| 
								 | 
							
								                for(int i = 0; i < fingerTips.Length; i++) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    fingerTips[i] = hand.transform.InverseTransformPoint(fingers[i].tip.position);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    Vector3 fingerTipWorld = hand.transform.TransformPoint(fingerTips[i]);
							 | 
						||
| 
								 | 
							
								                    float radius = fingers[i].tipRadius*fingerRadiusMultiplier*Mathf.Abs(hand.transform.lossyScale.x) * (isTouching[i] ? 2f : 1f);
							 | 
						||
| 
								 | 
							
								                    int numCollidersFinger = Physics.OverlapSphereNonAlloc(fingerTipWorld, radius, collidersNonAlloc, touchMask, QueryTriggerInteraction.Ignore);
							 | 
						||
| 
								 | 
							
								                    if (numCollidersFinger > 0) {
							 | 
						||
| 
								 | 
							
								                        var pressure = CalculateFingerTipPressure(fingerTipWorld, hand.transform.position, smoothAngularVelocity, hand.palmTransform.forward, out bool isForceTop, fingers[i].tipRadius);
							 | 
						||
| 
								 | 
							
								                        var evaluatedPressure = pressureBendCurve.Evaluate((pressure-minPressure)/maxPressure);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        isTouching[i] = true;
							 | 
						||
| 
								 | 
							
								                        if(pressure < minPressure)
							 | 
						||
| 
								 | 
							
								                            continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        //Difference between currentPressure[i] and evaluatedPressure
							 | 
						||
| 
								 | 
							
								                        float pressureDifference = Mathf.Abs(currentPressure[i] - evaluatedPressure);
							 | 
						||
| 
								 | 
							
								                        pressureDifference = Mathf.Pow(pressureDifference, 2f);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                        currentPressure[i] = Mathf.MoveTowards(currentPressure[i], evaluatedPressure * (isForceTop ? -1 : 1), pressureDifference * Time.fixedDeltaTime * 30f + Time.fixedDeltaTime * 5f);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    else {
							 | 
						||
| 
								 | 
							
								                        currentPressure[i] =  Mathf.MoveTowards(currentPressure[i], 0, maxOffsets[i]*Time.fixedDeltaTime*30f);
							 | 
						||
| 
								 | 
							
								                        isTouching[i] = false;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                for(int i = 0; i < fingers.Length; i++) {
							 | 
						||
| 
								 | 
							
								                    isTouching[i] = false;
							 | 
						||
| 
								 | 
							
								                    if(currentPressure[i] == 0)
							 | 
						||
| 
								 | 
							
								                        continue;
							 | 
						||
| 
								 | 
							
								                    currentPressure[i] = Mathf.MoveTowards(currentPressure[i], 0, maxOffsets[i]*Time.fixedDeltaTime*30f);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								       
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        Vector3 CalculateCustomAngularVelocity(Quaternion currentRotation, Quaternion targetRotation, float maxAngularVelocity) {
							 | 
						||
| 
								 | 
							
								            Quaternion rotationDifference = targetRotation * Quaternion.Inverse(currentRotation);
							 | 
						||
| 
								 | 
							
								            var eularAngularVelocity = rotationDifference.eulerAngles;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return eularAngularVelocity;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        float CalculateFingerTipPressure(Vector3 fingertipPoint, Vector3 wristPoint, Vector3 angularVelocity, Vector3 palmDirection, out bool isForceOnTop, float fingertipArea) {
							 | 
						||
| 
								 | 
							
								            Vector3 leverArm = fingertipPoint - wristPoint;
							 | 
						||
| 
								 | 
							
								            Vector3 torque = Vector3.Cross(leverArm, angularVelocity);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            float forceMagnitude = torque.magnitude / leverArm.magnitude;
							 | 
						||
| 
								 | 
							
								            float pressure = forceMagnitude / fingertipArea;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            Vector3 relativePoint;
							 | 
						||
| 
								 | 
							
								            if(float.IsNaN(angularVelocity.x) || float.IsNaN(angularVelocity.y) || float.IsNaN(angularVelocity.z))
							 | 
						||
| 
								 | 
							
								                relativePoint = fingertipPoint + hand.body.velocity*Time.fixedDeltaTime;
							 | 
						||
| 
								 | 
							
								            else 
							 | 
						||
| 
								 | 
							
								                relativePoint = Quaternion.Euler(angularVelocity)*leverArm + hand.body.velocity*Time.fixedDeltaTime;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var plane = new Plane(palmDirection, Vector3.zero);
							 | 
						||
| 
								 | 
							
								            isForceOnTop = plane.GetSide(relativePoint);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return pressure;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        void CreateEncapsulatingSphere(Vector3[] fingerTips, out Vector3 sphereCenter, out float sphereRadius) {
							 | 
						||
| 
								 | 
							
								            sphereCenter = new Vector3();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            foreach(var point in fingerTips) {
							 | 
						||
| 
								 | 
							
								                sphereCenter += point;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            sphereCenter /= fingerTips.Length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            sphereRadius = 0f;
							 | 
						||
| 
								 | 
							
								            foreach(var point in fingerTips) {
							 | 
						||
| 
								 | 
							
								                float distance = Vector3.Distance(sphereCenter, point);
							 | 
						||
| 
								 | 
							
								                sphereRadius = Mathf.Max(sphereRadius, distance);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |