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.
		
		
		
		
		
			
		
			
				
	
	
		
			333 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			333 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
using UnityEngine;
 | 
						|
 | 
						|
namespace Autohand{
 | 
						|
    [HelpURL("https://app.gitbook.com/s/5zKO0EvOjzUDeT2aiFk3/auto-hand/hand/finger-component")]
 | 
						|
    public class Finger : MonoBehaviour{
 | 
						|
        [Header("Tips")]
 | 
						|
        [Tooltip("This transfrom will represent the tip/stopper of the finger")]
 | 
						|
        public Transform tip;
 | 
						|
        [Tooltip("This determines the radius of the spherecast check when bending fingers")]
 | 
						|
        public float tipRadius = 0.01f;
 | 
						|
        [Tooltip("This will offset the fingers bend (0 is no bend, 1 is full bend)")]
 | 
						|
        [Range(0, 1f)]
 | 
						|
        public float bendOffset;
 | 
						|
        public float fingerSmoothSpeed = 1;
 | 
						|
 | 
						|
        [HideInInspector]
 | 
						|
        public float secondaryOffset = 0;
 | 
						|
        
 | 
						|
        float currBendOffset = 0;
 | 
						|
        float bend = 0;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [HideInInspector]
 | 
						|
        Quaternion[] minGripRotPose;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [HideInInspector]
 | 
						|
        Vector3[] minGripPosPose;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [HideInInspector]
 | 
						|
        Quaternion[] maxGripRotPose;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [HideInInspector]
 | 
						|
        Vector3[] maxGripPosPose;
 | 
						|
    
 | 
						|
        [SerializeField]
 | 
						|
        [HideInInspector]
 | 
						|
        Transform[] fingerJoints;
 | 
						|
 | 
						|
        public Transform[] FingerJoints { get { return fingerJoints; } }
 | 
						|
        
 | 
						|
        float lastHitBend;
 | 
						|
 | 
						|
        Collider[] results = new Collider[2];
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        void Update() {
 | 
						|
            SlowBend();
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        /// <summary>Forces the finger to a bend until it hits something on the given physics layer</summary>
 | 
						|
        /// <param name="steps">The number of steps and physics checks it will make lerping from 0 to 1</param>
 | 
						|
        public bool BendFingerUntilHit(int steps, int layermask) {
 | 
						|
            ResetBend();
 | 
						|
            lastHitBend = 0;
 | 
						|
 | 
						|
            for(float i = 0; i <= steps / 5f; i++) {
 | 
						|
                results[0] = null;
 | 
						|
                lastHitBend = i / (steps / 5f);
 | 
						|
                for(int j = 0; j < fingerJoints.Length; j++) {
 | 
						|
                    fingerJoints[j].localPosition = Vector3.Lerp(minGripPosPose[j], maxGripPosPose[j], lastHitBend);
 | 
						|
                    fingerJoints[j].localRotation = Quaternion.Lerp(minGripRotPose[j], maxGripRotPose[j], lastHitBend);
 | 
						|
                }
 | 
						|
                Physics.OverlapSphereNonAlloc(tip.transform.position, tipRadius, results, layermask, QueryTriggerInteraction.Ignore);
 | 
						|
 | 
						|
                if(results[0] != null) {
 | 
						|
                    lastHitBend = Mathf.Clamp01(lastHitBend);
 | 
						|
                    if(i == 0)
 | 
						|
                        return true;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
            }
 | 
						|
 | 
						|
 | 
						|
            lastHitBend -= (5f / steps);
 | 
						|
            for(int i = 0; i <= steps / 10f; i++) {
 | 
						|
                results[0] = null;
 | 
						|
                lastHitBend += (1f / steps);
 | 
						|
                for(int j = 0; j < fingerJoints.Length; j++) {
 | 
						|
                    fingerJoints[j].localPosition = Vector3.Lerp(minGripPosPose[j], maxGripPosPose[j], lastHitBend);
 | 
						|
                    fingerJoints[j].localRotation = Quaternion.Lerp(minGripRotPose[j], maxGripRotPose[j], lastHitBend);
 | 
						|
                }
 | 
						|
                Physics.OverlapSphereNonAlloc(tip.transform.position, tipRadius, results, layermask, QueryTriggerInteraction.Ignore);
 | 
						|
 | 
						|
 | 
						|
                if(results[0] != null) {
 | 
						|
                    bend = lastHitBend;
 | 
						|
                    currBendOffset = lastHitBend;
 | 
						|
                    lastHitBend = Mathf.Clamp01(lastHitBend);
 | 
						|
                    return true;
 | 
						|
                }
 | 
						|
 | 
						|
                if(lastHitBend >= 1) {
 | 
						|
                    lastHitBend = Mathf.Clamp01(lastHitBend);
 | 
						|
                    return true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        /// <summary>Bends the finger unless its hitting something</summary>
 | 
						|
        /// <param name="bend">0 is no bend / 1 is full bend</param>
 | 
						|
        public bool UpdateFingerBend(float bend, int layermask) {
 | 
						|
            var results = new Collider[]{ null };
 | 
						|
            Physics.OverlapSphereNonAlloc(tip.transform.position, tipRadius, results, layermask, QueryTriggerInteraction.Ignore);
 | 
						|
            if(this.bend > bend || results[0] == null){
 | 
						|
                this.bend = bend;
 | 
						|
                for(int i = 0; i < fingerJoints.Length; i++) {
 | 
						|
                    fingerJoints[i].localPosition = Vector3.Lerp(minGripPosPose[i], maxGripPosPose[i], currBendOffset+secondaryOffset);
 | 
						|
                    fingerJoints[i].localRotation = Quaternion.Lerp(minGripRotPose[i], maxGripRotPose[i], currBendOffset+secondaryOffset);
 | 
						|
                }
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        public void UpdateFinger() {
 | 
						|
            for(int i = 0; i < fingerJoints.Length; i++) {
 | 
						|
                fingerJoints[i].localPosition = Vector3.Lerp(minGripPosPose[i], maxGripPosPose[i], currBendOffset+secondaryOffset);
 | 
						|
                fingerJoints[i].localRotation = Quaternion.Lerp(minGripRotPose[i], maxGripRotPose[i], currBendOffset+secondaryOffset);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public void UpdateFinger(float bend) {
 | 
						|
            this.bend = bend;
 | 
						|
            for(int i = 0; i < fingerJoints.Length; i++) {
 | 
						|
                fingerJoints[i].localPosition = Vector3.Lerp(minGripPosPose[i], maxGripPosPose[i], currBendOffset+secondaryOffset);
 | 
						|
                fingerJoints[i].localRotation = Quaternion.Lerp(minGripRotPose[i], maxGripRotPose[i], currBendOffset+secondaryOffset);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>Forces the finger to a bend ignoring physics and offset</summary>
 | 
						|
        /// <param name="bend">0 is no bend / 1 is full bend</param>
 | 
						|
        public void SetFingerBend(float bend) {
 | 
						|
            this.bend = bend;
 | 
						|
            for(int i = 0; i < fingerJoints.Length; i++) {
 | 
						|
                fingerJoints[i].localPosition = Vector3.Lerp(minGripPosPose[i], maxGripPosPose[i], bend);
 | 
						|
                fingerJoints[i].localRotation = Quaternion.Lerp(minGripRotPose[i], maxGripRotPose[i], bend);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        /// <summary>Sets the current finger to a bend without interfering with the target</summary>
 | 
						|
         /// <param name="bend">0 is no bend / 1 is full bend</param>
 | 
						|
        public void SetCurrentFingerBend(float bend) {
 | 
						|
            currBendOffset = bend;
 | 
						|
            for(int i = 0; i < fingerJoints.Length; i++) {
 | 
						|
                fingerJoints[i].localPosition = Vector3.Lerp(minGripPosPose[i], maxGripPosPose[i], bend);
 | 
						|
                fingerJoints[i].localRotation = Quaternion.Lerp(minGripRotPose[i], maxGripRotPose[i], bend);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        //This function smooths the finger bend so you can change the grip over a frame and wont be a jump
 | 
						|
        void SlowBend(){
 | 
						|
 | 
						|
            var offsetValue = bendOffset + bend;
 | 
						|
            if(currBendOffset != offsetValue)
 | 
						|
                currBendOffset = Mathf.MoveTowards(currBendOffset, offsetValue, Mathf.Pow(Mathf.Abs(currBendOffset - offsetValue)*2, 0.5f) * Time.deltaTime * fingerSmoothSpeed * 6 + fingerSmoothSpeed * Time.deltaTime);
 | 
						|
        }
 | 
						|
    
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        [ContextMenu("ResetBend")]
 | 
						|
        public void ResetBend() {
 | 
						|
            for(int i = 0; i < fingerJoints.Length; i++) {
 | 
						|
                fingerJoints[i].localPosition = minGripPosPose[i];
 | 
						|
                fingerJoints[i].localRotation = minGripRotPose[i];
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        [ContextMenu("Grip")]
 | 
						|
        public void Grip() {
 | 
						|
            for(int i = 0; i < fingerJoints.Length; i++) {
 | 
						|
                fingerJoints[i].localPosition = maxGripPosPose[i];
 | 
						|
                fingerJoints[i].localRotation = maxGripRotPose[i];
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        /// <summary>Returns the bend the finger ended with from the last BendFingerUntilHit() call</summary>
 | 
						|
        public float GetLastHitBend() {
 | 
						|
            return lastHitBend;
 | 
						|
        }
 | 
						|
    
 | 
						|
 | 
						|
        [ContextMenu("Set Open Finger Pose")]
 | 
						|
        public void SetMinPose(){
 | 
						|
            int GetKidsCount(Transform obj, ref int count) {
 | 
						|
                if(obj != tip){
 | 
						|
                    count++;
 | 
						|
                    for(int k = 0; k < obj.childCount; k++) {
 | 
						|
                        GetKidsCount(obj.GetChild(k), ref count);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                return count;
 | 
						|
 | 
						|
            }
 | 
						|
 | 
						|
            int points = 0;
 | 
						|
            GetKidsCount(transform, ref points);
 | 
						|
            minGripPosPose = new Vector3[points];
 | 
						|
            minGripRotPose = new Quaternion[points];
 | 
						|
            fingerJoints = new Transform[points];
 | 
						|
            
 | 
						|
            int i = 0;
 | 
						|
            AssignChildrenPose(transform, ref i);
 | 
						|
            void AssignChildrenPose(Transform obj, ref int index) {
 | 
						|
                if(obj != tip){
 | 
						|
                    AssignPoint(index, obj.localPosition, obj.localRotation, obj);
 | 
						|
                    index++;
 | 
						|
                    for(int j = 0; j < obj.childCount; j++) {
 | 
						|
                        AssignChildrenPose(obj.GetChild(j), ref index);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            void AssignPoint(int point, Vector3 pos, Quaternion rot, Transform joint) {
 | 
						|
                minGripPosPose[point] = pos;
 | 
						|
                minGripRotPose[point] = rot;
 | 
						|
                fingerJoints[point] = joint;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
    
 | 
						|
        [ContextMenu("Set Closed Finger Pose")]
 | 
						|
        public void SetMaxPose(){
 | 
						|
            int GetKidsCount(Transform obj, ref int count) {
 | 
						|
                if(obj != tip){
 | 
						|
                    count++;
 | 
						|
                    for(int k = 0; k < obj.childCount; k++) {
 | 
						|
                        GetKidsCount(obj.GetChild(k), ref count);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                return count;
 | 
						|
            }
 | 
						|
 | 
						|
            int points = 0;
 | 
						|
            GetKidsCount(transform, ref points);
 | 
						|
            maxGripPosPose = new Vector3[points];
 | 
						|
            maxGripRotPose = new Quaternion[points];
 | 
						|
            fingerJoints = new Transform[points];
 | 
						|
 | 
						|
            int i = 0;
 | 
						|
            AssignChildrenPose(transform, ref i);
 | 
						|
            void AssignChildrenPose(Transform obj, ref int index){
 | 
						|
                if(obj != tip){
 | 
						|
                    AssignPoint(index, obj.localPosition, obj.localRotation, obj);
 | 
						|
                    index++;
 | 
						|
                    for(int j = 0; j < obj.childCount; j++) {
 | 
						|
                        AssignChildrenPose(obj.GetChild(j), ref index);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            void AssignPoint(int point, Vector3 pos, Quaternion rot, Transform joint) {
 | 
						|
                maxGripPosPose[point] = pos;
 | 
						|
                maxGripRotPose[point] = rot;
 | 
						|
                fingerJoints[point] = joint;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        public void CopyPose(Finger finger)
 | 
						|
        {
 | 
						|
            maxGripPosPose = new Vector3[finger.maxGripPosPose.Length];
 | 
						|
            finger.maxGripPosPose.CopyTo(maxGripPosPose, 0);
 | 
						|
            maxGripRotPose = new Quaternion[finger.maxGripRotPose.Length];
 | 
						|
            finger.maxGripRotPose.CopyTo(maxGripRotPose, 0);
 | 
						|
 | 
						|
            minGripPosPose = new Vector3[finger.minGripPosPose.Length];
 | 
						|
            finger.minGripPosPose.CopyTo(minGripPosPose, 0);
 | 
						|
            minGripRotPose = new Quaternion[finger.minGripRotPose.Length];
 | 
						|
            finger.minGripRotPose.CopyTo(minGripRotPose, 0);
 | 
						|
 | 
						|
            fingerJoints = new Transform[finger.fingerJoints.Length];
 | 
						|
            finger.fingerJoints.CopyTo(fingerJoints, 0);
 | 
						|
 | 
						|
        }
 | 
						|
        
 | 
						|
        public bool IsMinPoseSaved()
 | 
						|
        {
 | 
						|
            return minGripPosPose.Length != 0;
 | 
						|
        }
 | 
						|
        public bool IsMaxPoseSaved()
 | 
						|
        {
 | 
						|
            return maxGripPosPose.Length != 0;
 | 
						|
        }
 | 
						|
 | 
						|
        public float GetCurrentBend() {
 | 
						|
            return currBendOffset+secondaryOffset;
 | 
						|
        }
 | 
						|
    
 | 
						|
 | 
						|
        private void OnDrawGizmos() {
 | 
						|
            if(tip == null)
 | 
						|
                return;
 | 
						|
 | 
						|
            Gizmos.color = Color.cyan;
 | 
						|
            Gizmos.DrawWireSphere(tip.transform.position, tipRadius);
 | 
						|
        }
 | 
						|
 | 
						|
        private void OnDrawGizmosSelected()
 | 
						|
        {
 | 
						|
            Gizmos.color = Color.blue;
 | 
						|
            DrawSphereBetweenChild(transform);
 | 
						|
            void DrawSphereBetweenChild(Transform transform){
 | 
						|
                for (int i = 0; i < transform.childCount; i++)
 | 
						|
                {
 | 
						|
                    var childTransform = transform.GetChild(i);
 | 
						|
                    if (childTransform.TryGetComponent(out CapsuleCollider cap))
 | 
						|
                    {
 | 
						|
                        Gizmos.DrawWireSphere(Vector3.Lerp(transform.position, cap.bounds.center, 0.5f), tipRadius);
 | 
						|
                    }
 | 
						|
 | 
						|
                    DrawSphereBetweenChild(childTransform);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |