Advertisement
Vazili_KZ

Foot IK 1.3

Mar 11th, 2021
513
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 10.47 KB | None | 0 0
  1. using UnityEngine;
  2.  
  3. namespace AGD.Core.IK
  4. {
  5.     //TODO tune this system more to prevent foot from going inside a wall
  6.    
  7.     /// <summary>
  8.     /// To be placed on a character in order to add a foot IK grounding behaviour
  9.     /// Requires an Animator with a Layer set to IK Pass
  10.     /// </summary>
  11.     [RequireComponent(typeof(Animator))]
  12.     public class FootIK : MonoBehaviour
  13.     {
  14.         [Tooltip("Set to false and this script won't run.")]
  15.         [SerializeField] private bool applyFootIK = true;
  16.        
  17.         [Tooltip("Select the layer to which the foot IK will be applied")]
  18.         [SerializeField] private LayerMask targetLayer;
  19.  
  20.         [Tooltip("the distance of the ray check from the IK Foot Transform towards the ground")]
  21.         [Range(0,1)][SerializeField] private float checkDistance = 0;
  22.  
  23.         [Tooltip("the offset from the IK Foot Transform to the lowest point of the foot")]
  24.         [Range(0,1)][SerializeField] private float feetOffset = 0;
  25.  
  26.         [Header("Stairs Climbing Parameters")]
  27.         [Tooltip("The reference step height of the stairs")]
  28.         [Range(0, 1)] [SerializeField] private float stepHeight = .4f;
  29.  
  30.         private Animator _animator;
  31.         private Collider _collider;
  32.        
  33.        
  34.         private void Start()
  35.         {
  36.             _animator = GetComponent<Animator>();
  37.             _collider = GetComponent<Collider>();
  38.         }
  39.  
  40.         //Prevent collision with stairs non convex collider
  41.         private void OnCollisionEnter(Collision other)
  42.         {
  43.             if (other.gameObject.tag == "Stairs")
  44.             {
  45.                 Physics.IgnoreCollision(other.collider, _collider);
  46.             }
  47.         }
  48.  
  49.         private void OnAnimatorIK(int layerIndex)
  50.         {
  51.             if (!applyFootIK) return;
  52.             if (!_animator) return;
  53.            
  54.             // Set the weights of feet IK to the current value defined by the curve in the animations.
  55.             _animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, _animator.GetFloat("IKLeftFootWeight"));
  56.             _animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, _animator.GetFloat("IKLeftFootWeight"));
  57.             _animator.SetIKPositionWeight(AvatarIKGoal.RightFoot, _animator.GetFloat("IKRightFootWeight"));
  58.             _animator.SetIKRotationWeight(AvatarIKGoal.RightFoot, _animator.GetFloat("IKRightFootWeight"));
  59.            
  60.             //Variables used to calculated pelvis position after calculating the feet position
  61.             var leftFootYOffset = 0f;
  62.             var rightFootYOffset = 0f;
  63.            
  64.             //did the raycasts hitting a collider?
  65.             bool rayHit_1 = false;
  66.             bool rayHit_2 = false;
  67.            
  68.             //Setting IK Positions
  69.  
  70.             #region Left Foot
  71.            
  72.             //Raycasts starting from the center of the collider and going downward on the Y Axis passing through each foot.
  73.             RaycastHit hitInfo_1;
  74.             RaycastHit hitInfo_2;
  75.            
  76.             Ray ray_1 = new Ray(_animator.GetIKPosition(AvatarIKGoal.LeftFoot) + new Vector3(0,_collider.bounds.extents.y - feetOffset,0), Vector3.down);
  77.             Ray ray_2 = new Ray(_animator.GetIKPosition(AvatarIKGoal.LeftFoot) + new Vector3(0,_collider.bounds.extents.y - feetOffset,.15f), Vector3.down);
  78.            
  79.             Debug.DrawRay(_animator.GetIKPosition(AvatarIKGoal.LeftFoot) + new Vector3(0,_collider.bounds.extents.y - feetOffset,0), Vector3.down * (_collider.bounds.extents.y + checkDistance), Color.red);
  80.             Debug.DrawRay(_animator.GetIKPosition(AvatarIKGoal.LeftFoot) + new Vector3(0,_collider.bounds.extents.y - feetOffset,.15f), Vector3.down * (_collider.bounds.extents.y + checkDistance), Color.red);
  81.            
  82.             //Checking if rays are hitting something
  83.             if (Physics.Raycast(ray_1, out hitInfo_1, _collider.bounds.extents.y + checkDistance,
  84.                 targetLayer))
  85.             {
  86.                 rayHit_1 = true;
  87.             }
  88.             if (Physics.Raycast(ray_2, out hitInfo_2, _collider.bounds.extents.y + checkDistance,
  89.                 targetLayer))
  90.             {
  91.                 rayHit_2 = true;
  92.             }
  93.            
  94.             //If both
  95.             if (rayHit_1 && rayHit_2)
  96.             {
  97.                
  98.                 var hitDistance_1 = (hitInfo_1.point - _animator.GetIKPosition(AvatarIKGoal.LeftFoot)).magnitude;
  99.                 var hitDistance_2 = (hitInfo_2.point - new Vector3(0,0,.15f) - _animator.GetIKPosition(AvatarIKGoal.LeftFoot)).magnitude;
  100.                
  101.                
  102.                 //Case climbing stairs & only toes should be on a step
  103.                 if (hitDistance_1 >= stepHeight && hitDistance_2 < stepHeight)
  104.                 {
  105.                     //the length of the offset between the current foot position and it's target position
  106.                     leftFootYOffset = hitDistance_2;
  107.                    
  108.                     var footPosition = hitInfo_2.point - new Vector3(0,0,.15f); // same logic as above but with an offset that is the foot-toes distance.
  109.                     footPosition.y += feetOffset; //lifting the foot back up to compensate for the feet Offset.
  110.                     _animator.SetIKPosition(AvatarIKGoal.LeftFoot, footPosition);
  111.                     //Rotating the foot in a direction so that Vector3.up's rotation matches the hit surface's normal rotation.
  112.                     _animator.SetIKRotation(AvatarIKGoal.LeftFoot, Quaternion.FromToRotation(Vector3.up, hitInfo_2.normal));
  113.                 }
  114.                 else //the rest of the cases
  115.                 {
  116.                     //the length of the offset between the current foot position and it's target position
  117.                     leftFootYOffset = hitDistance_1;
  118.                    
  119.                     var footPosition = hitInfo_1.point; //The point in space where the ray hit the targetLayer ie: where the foot should be placed.
  120.                     footPosition.y += feetOffset; //lifting the foot back up to compensate for the feet Offset.
  121.                     _animator.SetIKPosition(AvatarIKGoal.LeftFoot, footPosition);
  122.                     //Rotating the foot in a direction so that Vector3.up's rotation matches the hit surface's normal rotation.
  123.                     _animator.SetIKRotation(AvatarIKGoal.LeftFoot, Quaternion.FromToRotation(Vector3.up, hitInfo_1.normal));
  124.                 }
  125.             }
  126.  
  127.             #endregion
  128.  
  129.             #region Right Foot
  130.  
  131.             //reset values
  132.             rayHit_1 = false;
  133.             rayHit_2 = false;
  134.            
  135.             //Raycasts starting from the center of the collider and going downward on the Y Axis passing through each foot.
  136.             ray_1 = new Ray(_animator.GetIKPosition(AvatarIKGoal.RightFoot) + new Vector3(0,_collider.bounds.extents.y - feetOffset,0), Vector3.down);
  137.             ray_2 = new Ray(_animator.GetIKPosition(AvatarIKGoal.RightFoot) + new Vector3(0,_collider.bounds.extents.y - feetOffset,.15f), Vector3.down);
  138.             Debug.DrawRay(_animator.GetIKPosition(AvatarIKGoal.RightFoot) + new Vector3(0,_collider.bounds.extents.y - feetOffset,0),
  139.                 Vector3.down * (_collider.bounds.extents.y + checkDistance), Color.red);
  140.             Debug.DrawRay(_animator.GetIKPosition(AvatarIKGoal.RightFoot) + new Vector3(0,_collider.bounds.extents.y - feetOffset,.15f),
  141.                 Vector3.down * (_collider.bounds.extents.y + checkDistance), Color.red);
  142.            
  143.             //Checking if rays are hitting something
  144.             if (Physics.Raycast(ray_1, out hitInfo_1, _collider.bounds.extents.y + checkDistance, targetLayer))
  145.             {
  146.                 rayHit_1 = true;
  147.             }
  148.             if (Physics.Raycast(ray_2, out hitInfo_2, _collider.bounds.extents.y + checkDistance, targetLayer))
  149.             {
  150.                 rayHit_2 = true;
  151.             }
  152.  
  153.             if (rayHit_1 && rayHit_2)
  154.             {
  155.                 var hitDistance_1 = (hitInfo_1.point - _animator.GetIKPosition(AvatarIKGoal.RightFoot)).magnitude;
  156.                 var hitDistance_2 = (hitInfo_2.point - new Vector3(0,0,.15f) - _animator.GetIKPosition(AvatarIKGoal.RightFoot)).magnitude;
  157.                 //Debug.Log("hitDistance_1 : " + hitDistance_1 + "hitDistance_2 : " + hitDistance_2);
  158.                
  159.                 //Case climbing stairs & only toes should be on a step
  160.                 if (hitDistance_1 >= stepHeight && hitDistance_2 < stepHeight)
  161.                 {
  162.                     //the length of the offset between the current foot position and it's target position
  163.                     rightFootYOffset = hitDistance_2;
  164.                    
  165.                     var footPosition = hitInfo_2.point - new Vector3(0,0,.15f); // same logic as above but with an offset that is the foot-toes distance.
  166.                     footPosition.y += feetOffset; //lifting the foot back up to compensate for the feet Offset.
  167.                     _animator.SetIKPosition(AvatarIKGoal.RightFoot, footPosition);
  168.                     //Rotating the foot in a direction so that Vector3.up's rotation matches the hit surface's normal rotation.
  169.                     _animator.SetIKRotation(AvatarIKGoal.RightFoot, Quaternion.FromToRotation(Vector3.up, hitInfo_2.normal));
  170.                 }
  171.                 else //the rest of the cases
  172.                 {
  173.                     //the length of the offset between the current foot position and it's target position
  174.                     rightFootYOffset = hitDistance_1;
  175.                    
  176.                     var footPosition = hitInfo_1.point; //The point in space where the ray hit the targetLayer ie: where the foot should be placed.
  177.                     footPosition.y += feetOffset; //lifting the foot back up to compensate for the feet Offset.
  178.                     _animator.SetIKPosition(AvatarIKGoal.RightFoot, footPosition);
  179.                     //Rotating the foot in a direction so that Vector3.up's rotation matches the hit surface's normal rotation.
  180.                     _animator.SetIKRotation(AvatarIKGoal.RightFoot, Quaternion.FromToRotation(Vector3.up, hitInfo_1.normal));
  181.                 }
  182.             }
  183.             #endregion
  184.  
  185.             #region Pelvis
  186.  
  187.             var pelvisYOffset = Mathf.Max(leftFootYOffset, rightFootYOffset);
  188.             var pelvisOffset = new Vector3(0,pelvisYOffset,0);
  189.             //var target = Vector3.Lerp(_animator.bodyPosition, _animator.bodyPosition - pelvisOffset, .15f);
  190.             _animator.bodyPosition -= pelvisOffset * _animator.GetFloat("IKPelvisWeight");
  191.  
  192.             #endregion
  193.         }
  194.     }
  195. }
  196.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement