Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Using IK why the head is not getting back to natural position smooth?

Discussion in 'Animation' started by shamenraze1988, Jul 23, 2021.

  1. shamenraze1988

    shamenraze1988

    Joined:
    Nov 24, 2020
    Posts:
    208
    The question is how to handle this case and make the player's head and body get back to the natural position smoothly.

    The script is a bit long but I show the exact lines with the problem/s.

    This screenshot shows the players settings in the inspector while the game is running and the player is looking at the target cube :



    This is how the player is looking at the target and when I'm moving the target around the player is smoothly looking at it :



    Then I'm dragging the target above the player head and a bit to the back: The cube is now above the player and the player is looking up :



    At this point if I will move the cube a bit by one step to the back the player head will move to natural look to its original idle look position at once like the head is jumping to the original position and not moving smoothly to the original natural position :



    From line 184 is where the player head and body should be a return to the natural look :

    // Let the player smoothly look away from the last target to the neutral look position

    I just don't understand why the head and body are not returning to the natural look position smoothly? And how to fix that problem?

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System;
    4. using System.Collections;
    5. using UnityEngine.UI;
    6. using System.Collections.Generic;
    7. using System.Linq;
    8. using TMPro;
    9.  
    10. [RequireComponent(typeof(Animator))]
    11. public class IKControl : MonoBehaviour
    12. {
    13.    public List<InteractableItem> lookObjs = new List<InteractableItem>();
    14.    public TextMeshProUGUI text;
    15.    public float weightDamping = 1.5f;
    16.    public bool RightHandToTarget = true;
    17.    public float throwSpeed;
    18.    public bool handFinishedMove = false;
    19.    public GameObject descriptionTextImage;
    20.    public GameObject naviParent;
    21.    public DimLights dimLights;
    22.    public Light[] naviLights;
    23.    public bool startMovingNAVI = false;
    24.    public InteractableItem target;
    25.    public bool toTarget = false;
    26.    public bool hasSent = false;
    27.    public InteractableItem leftHand;
    28.  
    29.    private List<InteractableItem> allDetectedItems;
    30.    private Animator animator;
    31.    private InteractableItem lastPrimaryTarget;
    32.    private float lerpEndDistance = 0.1f;
    33.    private float finalLookWeight = 0;
    34.    private bool transitionToNextTarget = false;
    35.    private List<string> objectsNames = new List<string>();
    36.    private bool setNaviRotation = false;
    37.  
    38.    private void Awake()
    39.    {
    40.  
    41.    }
    42.  
    43.    void Start()
    44.    {
    45.        animator = GetComponent<Animator>();
    46.        allDetectedItems = new List<InteractableItem>();
    47.  
    48.        dimLights.lightsToDim = naviLights;
    49.    }
    50.  
    51.    // Callback for calculating IK
    52.    void OnAnimatorIK()
    53.    {
    54.        if (lookObjs != null)
    55.        {
    56.            lookObjs.RemoveAll(x => x == null);
    57.  
    58.            InteractableItem primaryTarget = null;
    59.  
    60.            float closestLookWeight = 0;
    61.  
    62.            // Here we find the target which is closest (by angle) to the players view line
    63.            allDetectedItems.Clear();
    64.            foreach (InteractableItem target in lookObjs)
    65.            {
    66.                Vector3 lookAt = target.transform.position - transform.position;
    67.                lookAt.y = 0f;
    68.  
    69.                // Filter out all objects that are too far away
    70.                if (lookAt.magnitude > target.distance) continue;
    71.  
    72.                float dotProduct = Vector3.Dot(new Vector3(transform.forward.x, 0f, transform.forward.z).normalized, lookAt.normalized);
    73.                float lookWeight = Mathf.Clamp(dotProduct, 0f, 1f);
    74.                if (lookWeight > 0.1f && lookWeight > closestLookWeight)
    75.                {
    76.                    closestLookWeight = lookWeight;
    77.                    primaryTarget = target;
    78.                }
    79.  
    80.                allDetectedItems.Add(target);
    81.            }
    82.  
    83.            if (allDetectedItems.Count == 2)
    84.            {
    85.                objectsNames = allDetectedItems.Select(item => item.transform.name).ToList();
    86.                if (objectsNames.Contains("NAVI") && objectsNames.Contains("Security Keypad Interactable"))
    87.                {
    88.                    InteractableItem navi = allDetectedItems.Single(item => item.transform.name == "NAVI");
    89.                    InteractableItem securitykeypad = allDetectedItems.Single(item => item.transform.name == "Security Keypad Interactable");
    90.                    var crate = GameObject.Find("Crate_0_0");
    91.  
    92.                    if (crate.gameObject.GetComponent<UnlockCrate>().HasOpened())
    93.                    {
    94.                        primaryTarget = navi;
    95.                        target = primaryTarget;
    96.                        startMovingNAVI = true;
    97.                    }
    98.                    else
    99.                    {
    100.                        primaryTarget = securitykeypad;
    101.                    }
    102.                }
    103.            }
    104.  
    105.            InteractWithTarget(primaryTarget, closestLookWeight);
    106.        }
    107.    }
    108.  
    109.    private void InteractWithTarget(InteractableItem primaryTarget, float closestLookWeight)
    110.    {
    111.        if (primaryTarget != null)
    112.        {
    113.            if ((lastPrimaryTarget != null) && (lastPrimaryTarget != primaryTarget) && (finalLookWeight > 0f))
    114.            {
    115.                // Here we start a new transition because the player looks already to a target but
    116.                // we have found another target the player should look at
    117.                transitionToNextTarget = true;
    118.            }
    119.        }
    120.  
    121.        // The player is in a neutral look position but has found a new target
    122.        if ((primaryTarget != null) && !transitionToNextTarget)
    123.        {
    124.            if (primaryTarget.IsAnyAction())//.interactableMode == InteractableItem.InteractableMode.ActionWithoutThrow)
    125.            {
    126.                RightHandToTarget = true;
    127.            }
    128.  
    129.            lastPrimaryTarget = primaryTarget;
    130.            //finalLookWeight = Mathf.Lerp(finalLookWeight, closestLookWeight, Time.deltaTime * weightDamping);
    131.            finalLookWeight = Mathf.Lerp(finalLookWeight, 1f, Time.deltaTime * weightDamping);
    132.            float bodyWeight = finalLookWeight * .1f;
    133.            animator.SetLookAtWeight(finalLookWeight, bodyWeight, 1f);
    134.            animator.SetLookAtPosition(primaryTarget.transform.position);
    135.  
    136.            if (RightHandToTarget && primaryTarget.IsAnyAction())
    137.            {
    138.                Vector3 relativePos = primaryTarget.transform.position - transform.position;
    139.                Quaternion rotationtoTarget = Quaternion.LookRotation(relativePos, Vector3.up);
    140.  
    141.                if (primaryTarget.interactableMode == InteractableItem.InteractableMode.ActionWithoutThrow)
    142.                {
    143.                    animator.SetIKRotationWeight(AvatarIKGoal.RightHand, finalLookWeight);
    144.                    animator.SetIKRotation(AvatarIKGoal.RightHand, rotationtoTarget);
    145.                    animator.SetIKPositionWeight(AvatarIKGoal.RightHand, finalLookWeight * 1f * closestLookWeight);
    146.                    animator.SetIKPosition(AvatarIKGoal.RightHand, primaryTarget.transform.position);
    147.                }
    148.  
    149.                if (primaryTarget.interactableMode == InteractableItem.InteractableMode.Action)
    150.                {
    151.                    animator.SetIKRotationWeight(AvatarIKGoal.RightHand, finalLookWeight);
    152.                    animator.SetIKRotation(AvatarIKGoal.RightHand, rotationtoTarget);
    153.                    animator.SetIKPositionWeight(AvatarIKGoal.RightHand, finalLookWeight * 0.1f * closestLookWeight);
    154.                    animator.SetIKPosition(AvatarIKGoal.RightHand, primaryTarget.transform.position);
    155.                }
    156.  
    157.                // -> new code block
    158.                if (finalLookWeight > 0.95f) // here you can play with a value between 0.95f -> 1.0f
    159.                {
    160.                    if (primaryTarget.interactableMode == InteractableItem.InteractableMode.Action
    161.                        && hasSent == false)
    162.                    {
    163.                        target = primaryTarget;
    164.                        toTarget = true;
    165.                        hasSent = true;
    166.                        primaryTarget.description = "";
    167.                        // Here I need to find where to make hasSent false again.
    168.                    }
    169.                }
    170.                else
    171.                {
    172.                    hasSent = false;
    173.                }
    174.  
    175.                if (finalLookWeight > 0.9f)
    176.                {
    177.                    handFinishedMove = true;
    178.                }
    179.            }
    180.        }
    181.  
    182.        // Let the player smoothly look away from the last target to the neutral look position
    183.        if ((primaryTarget == null && lastPrimaryTarget != null) || transitionToNextTarget)
    184.        {
    185.            finalLookWeight = Mathf.Lerp(finalLookWeight, 0f, Time.deltaTime * weightDamping);
    186.            float bodyWeight = finalLookWeight * .75f;
    187.            animator.SetLookAtWeight(finalLookWeight, bodyWeight, 1f);
    188.            animator.SetLookAtPosition(lastPrimaryTarget.transform.position);
    189.  
    190.            if (RightHandToTarget)
    191.            {
    192.                Vector3 relativePos = lastPrimaryTarget.transform.position - transform.position;
    193.                Quaternion rotationtoTarget = Quaternion.LookRotation(relativePos, Vector3.up);
    194.                animator.SetIKRotationWeight(AvatarIKGoal.RightHand, finalLookWeight);
    195.                animator.SetIKRotation(AvatarIKGoal.RightHand, rotationtoTarget);
    196.                animator.SetIKPositionWeight(AvatarIKGoal.RightHand, finalLookWeight * 0.5f * closestLookWeight);
    197.                animator.SetIKPosition(AvatarIKGoal.RightHand, lastPrimaryTarget.transform.position);
    198.            }
    199.  
    200.            if (finalLookWeight < lerpEndDistance)
    201.            {
    202.                transitionToNextTarget = false;
    203.                finalLookWeight = 0f;
    204.                lastPrimaryTarget = null;
    205.                transform.rotation = Quaternion.Euler(0, transform.eulerAngles.y, 0);
    206.            }
    207.        }
    208.  
    209.        if (text != null)
    210.        {
    211.            // Show primary object found by the player
    212.            if (primaryTarget != null)
    213.            {
    214.                if (primaryTarget.description != "")
    215.                {
    216.                    descriptionTextImage.SetActive(true);
    217.                    text.text = primaryTarget.description;
    218.                }
    219.            }
    220.            else
    221.            {
    222.                text.text = "";
    223.                descriptionTextImage.SetActive(false);
    224.            }
    225.        }
    226.    }
    227.  
    228.    private void Update()
    229.    {
    230.        if (MenuController.LoadSceneForSavedGame == true && setNaviRotation == false)
    231.        {
    232.            transform.rotation = Quaternion.Euler(0, transform.eulerAngles.y, 0);
    233.            setNaviRotation = true;
    234.        }
    235.    }
    236. }
    237.