Search Unity

Question Feet IK failing for a single frame

Discussion in 'Scripting' started by Jankaro, Dec 7, 2023.

  1. Jankaro

    Jankaro

    Joined:
    Jan 5, 2019
    Posts:
    4
    Hi!

    I'm making a feet IK script in my own so it covers all my needs. I manage to make it work in all case scenarios but one... And thought that maybe someone would have the key to solve it. Also feel free to use the code I'm going to share if you find it useful.
    Let me explain the problem. I'm using a Character Controller to move the player and in one of it's childs is the model with my IK script.
    Captura de pantalla 2023-12-07 232645.png
    Character Controller can climb steps so when parent Transform move also do the childs. I manage to solve this so it's a smooth transition instead of an instant movement, but... the first frame. The first frame character controller step up the whole body moves with the parent, and the next frame it reposition correctly.
    This is the actual script I'm using:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class BetterIKFootPlacement : MonoBehaviour
    6. {
    7.     [SerializeField] LayerMask walkableLayers;
    8.     [SerializeField] Transform leftFoot;
    9.     [SerializeField] Transform rightFoot;
    10.     [Tooltip("Maximum local altitude the body can be")]
    11.     [SerializeField] float maxBodyHeight = -0.1f;
    12.     [Tooltip("Maximum distance can be between feets")]
    13.     [SerializeField] float maxFootDistance = 0.5f;
    14.     [Tooltip("Distance from where the foot transform is to the lowest possible position of the foot.")]
    15.     [Range(0, 1f)]
    16.     [SerializeField] float distanceToGround = 0.1f;
    17.     [SerializeField] float bodySmoothTime = 0.5f;
    18.  
    19.     private Animator anim;
    20.     private float bodyCV = 0;
    21.     private float lastBodyHeight = 0;
    22.     private Vector3 lastLeftRPos = Vector3.zero;
    23.  
    24.     void Awake()
    25.     {
    26.         anim = GetComponent<Animator>();
    27.     }
    28.  
    29.     void Start()
    30.     {
    31.         lastBodyHeight = transform.position.y;
    32.     }
    33.  
    34.     void OnAnimatorIK(int layerIndex)
    35.     {
    36.         if (anim == null) return;
    37.  
    38.         transform.position = new Vector3(transform.position.x, lastBodyHeight, transform.position.z); ;
    39.  
    40.         UpdateFootIK(AvatarIKGoal.LeftFoot, leftFoot);
    41.         UpdateFootIK(AvatarIKGoal.RightFoot, rightFoot);
    42.     }
    43.  
    44.     void LateUpdate()
    45.     {
    46.         // Calculate target body height based on the lowest foot position
    47.         float targetBodyHeight = Mathf.Min(leftFoot.position.y, rightFoot.position.y);
    48.  
    49.         // Smoothly adjust the current body height towards the target body height
    50.         float smoothHeight =
    51.             Mathf.SmoothDamp(transform.position.y, targetBodyHeight - distanceToGround, ref bodyCV, bodySmoothTime);
    52.         transform.position = new Vector3(transform.position.x, smoothHeight, transform.position.z);
    53.  
    54.         // Ensure local position is based on the foot positions
    55.         Vector3 localPos = transform.localPosition;
    56.         localPos.y = Mathf.Clamp(localPos.y, maxBodyHeight - maxFootDistance, maxBodyHeight);
    57.         transform.localPosition = localPos;
    58.  
    59.         lastBodyHeight = transform.position.y;
    60.     }
    61.  
    62.     private void UpdateFootIK(AvatarIKGoal goal, Transform foot)
    63.     {
    64.         float footWeight = anim.GetFloat($"IK{goal}Weight");
    65.         RaycastHit hit;
    66.  
    67.         if (Physics.SphereCast(foot.position + Vector3.up * maxFootDistance, distanceToGround, Vector3.down, out hit, maxFootDistance + distanceToGround * 2, walkableLayers) &&
    68.             (foot.position.y < hit.point.y || Mathf.Abs(foot.position.y - hit.point.y) <= distanceToGround))
    69.         {
    70.             anim.SetIKPositionWeight(goal, 1);
    71.             anim.SetIKRotationWeight(goal, 1);
    72.         }
    73.         else
    74.         {
    75.             anim.SetIKPositionWeight(goal, footWeight);
    76.             anim.SetIKRotationWeight(goal, footWeight);
    77.         }
    78.  
    79.         Ray ray = new Ray(anim.GetIKPosition(goal) + Vector3.up * maxFootDistance, Vector3.down);
    80.         Debug.DrawRay(ray.origin, ray.direction, Color.yellow);
    81.  
    82.         if (Physics.Raycast(ray, out hit, Mathf.Infinity, walkableLayers))
    83.         {
    84.             Vector3 footDesiredPos = hit.point + Vector3.up * distanceToGround;
    85.             anim.SetIKPosition(goal, footDesiredPos);
    86.             Vector3 forward = Vector3.ProjectOnPlane(transform.forward, hit.normal);
    87.             anim.SetIKRotation(goal, Quaternion.LookRotation(forward, hit.normal));
    88.         }
    89.     }
    90. }
    91.  
    Also I leave some images for better context.
    Captura de pantalla 2023-12-07 232730.png

    Captura de pantalla 2023-12-07 232707.png

    Thanks in advance!
     

    Attached Files: