Search Unity

  1. We are migrating the Unity Forums to Unity Discussions by the end of July. Read our announcement for more information and let us know if you have any questions.
    Dismiss Notice
  2. Dismiss Notice

Resolved Procedural animation for a crab with Rigging animation & Inverse kinematic

Discussion in 'Animation' started by maxime66410, Jun 27, 2023.

  1. maxime66410

    maxime66410

    Joined:
    Mar 16, 2018
    Posts:
    19
    I work on unity, I have to completely change my script which allows to do procedural animation in reverse kinematic, in fact to explain to you I have a crab with 8 legs, each with 4 pivot points, and I want to do so that when the crab moves the legs also, via procedural animation.

    I have already created a script to do all this, however sometimes the point remains in place, impossible to reset it, even with checks, whether in the calculation, out of the calculation, and even by forcing with Tasks etc..

    So I would like to know if some here could help me fix this bug, or completely redo everything to start from a good base.

    Here is my full script.

    Code (CSharp):
    1. // ReSharper disable once InvalidXmlDocComment
    2. /**
    3. * Original Script by: Sanchez Maxime (@Maxime66410)
    4. * @version 1.0.0
    5. * @autor Sanchez Maxime (@Maxime66410)
    6. *
    7. * Last modification: 09/06/2023
    8. * File description: This script is used to procedurally animate the crab
    9. */
    10.  
    11. using System;
    12. using System.Collections.Generic;
    13. using System.Threading.Tasks;
    14. using UnityEngine;
    15. using UnityEngine.Serialization;
    16.  
    17. // ReSharper disable once CheckNamespace
    18. public class CrabPatteCombine : MonoBehaviour
    19. {
    20.       [FormerlySerializedAs("SingleLegs")] [Header("Legs GameObject"), Space(10)]
    21.       public List<GameObject> singleLegs = new List<GameObject>(); // Paw GameObject
    22.       [FormerlySerializedAs("SingleLegsIK")] public List<GameObject> singleLegsIK = new List<GameObject>(); // IK's GameObject
    23.  
    24.       [FormerlySerializedAs("SingleLegsTarget")] [Header("Vector3"), Space(10)]
    25.       public List<Vector3> singleLegsTarget = new List<Vector3>(); // Leg position
    26.       [FormerlySerializedAs("SingleLocalTarget")] public List<Vector3> singleLocalTarget = new List<Vector3>(); // Position of the IK
    27.      
    28.       [Header("Legs / Step"), Space(10)]
    29.       [FormerlySerializedAs("SpeedSyncLegs")] public float speedSyncLegs = 10f; // paw speed
    30.       [FormerlySerializedAs("DistanceToPoint")] public float distanceToPoint = 0.075f; // Distance between paw and target
    31.       [FormerlySerializedAs("StepHeight")] public float stepHeight = 0.5f; // step height
    32.       [FormerlySerializedAs("StepHeightCurve")] public AnimationCurve stepHeightCurve; // Step height curve
    33.       [FormerlySerializedAs("OffsetStep")] public Vector3 offsetStep = Vector3.zero; // Step height offset
    34.       [FormerlySerializedAs("_distanceStep")] [SerializeField] private List<float> distanceStep = new List<float>(); // Distance between paw and target
    35.       [FormerlySerializedAs("_distanceToLegFromIK")] [SerializeField] private List<float> distanceToLegFromIK = new List<float>(); // Distance between paw and target
    36.       [FormerlySerializedAs("DistanceToStopSyncLeg")] public float distanceToStopSyncLeg = 0.01f; // Distance between paw and target to stop synchronization
    37.       [FormerlySerializedAs("MaxDistanceToSyncLeg")] public float maxDistanceToSyncLeg = 1f; // Maximum distance between paw and target to synchronize the paw
    38.       [FormerlySerializedAs("MaxDistanceToSyncLeg")] public float maxDistanceDebug = 0.2f; // Maximum distance between paw and target to synchronize the paw (Debug)
    39.       [FormerlySerializedAs("RaycastDistance")] public float raycastDistance = 0.3f; // Raycast distance
    40.       [FormerlySerializedAs("LayerMaskGround")] public LayerMask layerMaskGround; // LayerMask
    41.      
    42.       public void Awake()
    43.       {
    44.           // Automatic initialization of the lists
    45.           for (int i = 0; i < singleLegs.Count; i++)
    46.           {
    47.               distanceStep.Add(0f);
    48.               distanceToLegFromIK.Add(0f);
    49.               singleLegsTarget.Add(Vector3.zero);
    50.               singleLocalTarget.Add(Vector3.zero);
    51.           }
    52.       }
    53.      
    54.       public void Start()
    55.       {
    56.           GetSurfaceOnStart();
    57. #pragma warning disable CS4014
    58.           CheckDistanceLeg();
    59. #pragma warning restore CS4014
    60.       }
    61.      
    62.       // Get crab area at start Position and rotation
    63.       public void GetSurfaceOnStart()
    64.       {
    65.           // If SingleLegs is not empty
    66.           if (singleLegs.Count > 0)
    67.           {
    68.               for (int i = 0; i < singleLegs.Count; i++)
    69.               {
    70.                   singleLocalTarget[i] = singleLegsIK[i].transform.position; // Get IK paw position
    71.  
    72.                   // Get the surface of the starting crab Position and rotation
    73.                   RaycastHit hit;
    74.                   if (Physics.Raycast(transform.position, Vector3.down, out hit, 0.03f, layerMaskGround))
    75.                   {
    76.                       singleLegs[i].transform.position = hit.point;
    77.                       singleLegsTarget[i] = hit.point;
    78.                       Transform transform1 = transform;
    79.                       transform1.rotation = Quaternion.FromToRotation(transform1.up, hit.normal) * (transform).rotation;
    80.                   }
    81.               }
    82.           }
    83.       }
    84.      
    85.       public void FixedUpdate()
    86.       {
    87.           CheckDistance();
    88.           UpdatePosition();
    89.       }
    90.      
    91.       // Check the distance between the leg and the target
    92.       public void CheckDistance()
    93.       {
    94.           // If SingleLegs is not empty
    95.           if(singleLegs.Count > 0)
    96.           {
    97.               // Check the distance between the leg and the target
    98.               for (int i = 0; i < singleLegs.Count; i++)
    99.               {
    100.                   RaycastHit hit;
    101.                   if (Physics.Raycast(singleLocalTarget[i], Vector3.down, out hit, raycastDistance, layerMaskGround))
    102.                   {
    103.                       distanceStep[i] = Vector3.Distance(singleLegs[i].transform.position, hit.point);
    104.                       distanceToLegFromIK[i] = Vector3.Distance(singleLegs[i].transform.localPosition, singleLegsIK[i].transform.localPosition);
    105.                
    106.                
    107.                       // If the remaining distance is really too much greater than the target distance, reset the leg position (Fix Bug)
    108.                       if (distanceToLegFromIK[i] > maxDistanceToSyncLeg)
    109.                       {
    110.                           singleLegs[i].transform.position = singleLegsIK[i].transform.position;
    111.                           singleLegsTarget[i] = singleLegsIK[i].transform.position;
    112.                           singleLocalTarget[i] = singleLegsIK[i].transform.position;
    113.                       }
    114.                       else
    115.                       {
    116.                           // If the distance is greater than DistanceToPoint, update the leg position
    117.                           if (Vector3.Distance(singleLegs[i].transform.position, hit.point) > distanceToPoint)
    118.                           {
    119.                               singleLegsTarget[i] = hit.point;
    120.                           }
    121.                       }
    122.                   }
    123.               }
    124.           }
    125.       }
    126.  
    127.       // Update leg position
    128.       public void UpdatePosition()
    129.       {
    130.           if (singleLegs.Count > 0)
    131.           {
    132.               for (int i = 0; i < singleLegs.Count; i++)
    133.               {
    134.                   if (singleLegs[i].transform.position != singleLegsTarget[i])
    135.                   {
    136.                       // Calculate the remaining distance between the current leg position and the target
    137.                       float remainingDistance = Vector3.Distance(singleLegs[i].transform.position, singleLegsTarget[i]);
    138.  
    139.                       // Calculate total step based on remaining distance
    140.                       float totalStep = distanceToPoint * stepHeightCurve.length;
    141.  
    142.                       // If the remaining distance is less than the total step, adjust the height of the step
    143.                       if (remainingDistance < totalStep)
    144.                       {
    145.                           // Calculate step height using StepHeightCurve
    146.                           // ReSharper disable once LocalVariableHidesMember
    147.                           float stepHeight = stepHeightCurve.Evaluate(1f - (remainingDistance / totalStep)) * this.stepHeight;
    148.  
    149.                           // Move paw to target with height adjustment
    150.                           singleLegs[i].transform.position = Vector3.Lerp(singleLegs[i].transform.position, singleLegsTarget[i], Time.deltaTime * speedSyncLegs) + (offsetStep * stepHeight);
    151.  
    152.                           // Adjust paw height to simulate lifting motion
    153.                           singleLegs[i].transform.position += Vector3.up * stepHeight;
    154.  
    155.                           // If the remaining distance is less than a small threshold value, reset the position of the paw
    156.                           if (remainingDistance < distanceToStopSyncLeg)
    157.                           {
    158.                               singleLegs[i].transform.position = singleLegsTarget[i];
    159.                           }
    160.                            
    161.                           // If the remaining distance is really too much greater than the distance to the target, reset the position of the paw
    162.                           if (remainingDistance > maxDistanceToSyncLeg)
    163.                           {
    164.                               singleLegs[i].transform.position = singleLegsIK[i].transform.position;
    165.                           }
    166.                       }
    167.                       else
    168.                       {
    169.                           // Move paw to target without height adjustment
    170.                           singleLegs[i].transform.position = Vector3.Lerp(singleLegs[i].transform.position, singleLegsTarget[i], Time.deltaTime * speedSyncLegs);
    171.                            
    172.                           FixDistanceLeg();
    173.                       }
    174.                   }
    175.                   else
    176.                   {
    177.                       // If the remaining distance is really too much greater than the distance to the target, reset the position of the paw
    178.                       float remainingDistance = Vector3.Distance(singleLegs[i].transform.position, singleLegsIK[i].transform.position);
    179.                        
    180.                       // If the remaining distance is really too much greater than the distance to the target, reset the position of the paw
    181.                       if (remainingDistance > maxDistanceToSyncLeg)
    182.                       {
    183.                           singleLegs[i].transform.position = Vector3.Lerp(singleLegs[i].transform.position, singleLegsIK[i].transform.position, Time.deltaTime * speedSyncLegs);
    184.                       }
    185.                   }
    186.                    
    187.                   // Get the ground surface below the leg and adjust the local target
    188.                   RaycastHit hit;
    189.                   if (Physics.Raycast(singleLegsIK[i].transform.position, Vector3.down, out hit, raycastDistance, layerMaskGround))
    190.                   {
    191.                       singleLocalTarget[i] = Vector3.Lerp(singleLocalTarget[i], new Vector3(singleLegsIK[i].transform.position.x, hit.point.y, singleLegsIK[i].transform.position.z), Time.deltaTime * speedSyncLegs);
    192.                   }
    193.  
    194.                   FixDistanceLeg();
    195.               }
    196.           }
    197.  
    198.           // SingleLocalTarget = SingleLegsIK.transform.position; // OLD
    199.       }
    200.      
    201.       // If the remaining distance is really too much greater than the target distance, reset the leg position (Fix Bug)
    202.       public void FixDistanceLeg()
    203.       {
    204.           for (int i = 0; i < singleLegs.Count; i++)
    205.           {
    206.               if (distanceToLegFromIK[i] > maxDistanceToSyncLeg)
    207.               {
    208.                   singleLegs[i].transform.position = singleLegsIK[i].transform.position;
    209.                   singleLegsTarget[i] = singleLegsIK[i].transform.position;
    210.                   singleLocalTarget[i] = singleLegsIK[i].transform.position;
    211.               }
    212.           }
    213.       }
    214.      
    215.       // ReSharper disable once FunctionRecursiveOnAllPaths
    216.       public async Task CheckDistanceLeg()
    217.       {
    218.           for (int i = 0; i < singleLegs.Count; i++)
    219.           {
    220.               // If the distance between the paw and the target is greater than the maximum distance, reset the position of the paw
    221.               float distance = Vector3.Distance(singleLegs[i].transform.position, singleLegsIK[i].transform.position);
    222.               //Debug.Log($"Distance to Leg + {singleLegsIK[i].name} = {distance}");
    223.               if(distance > maxDistanceDebug)
    224.               {
    225.                   singleLegs[i].transform.position = singleLegsIK[i].transform.position;
    226.                   singleLegsTarget[i] = singleLegsIK[i].transform.position;
    227.                   singleLocalTarget[i] = singleLegsIK[i].transform.position;
    228.               }
    229.           }
    230.          
    231.  
    232.           await Task.Delay(TimeSpan.FromSeconds(1));
    233.  
    234.           await CheckDistanceLeg();
    235.       }
    236.  
    237.       // Draw a line between the paw and the target
    238.       public void OnDrawGizmos()
    239.       {
    240.           if (singleLocalTarget.Count > 0)
    241.           {
    242.               for (int i = 0; i < singleLegs.Count; i++)
    243.               {
    244.                   if (singleLocalTarget[i] != Vector3.zero)
    245.                   {
    246.                       Gizmos.color = Color.magenta;
    247.                       Gizmos.DrawLine(singleLegs[i].transform.position, singleLocalTarget[i]);
    248.                   }
    249.        
    250.                   if(singleLocalTarget[i] != Vector3.zero)
    251.                   {
    252.                       Gizmos.color = Color.red;
    253.                       Gizmos.DrawLine(singleLocalTarget[i], singleLocalTarget[i] + Vector3.down * raycastDistance);
    254.                   }
    255.               }
    256.           }
    257.       }
    258. }
    259.  
    ShowCase :
    https://media.discordapp.net/attach...58472243/bb4ec7b4f30a5931a16048c665ab1904.mp4

    https://cdn.discordapp.com/attachme...40825425/abb57db906ed7d7ef2663f3e6fc1745d.mp4

    Here is my problem :
    As you can see, sometimes the red cube remains frozen.

    Since I've been stuck on a small problem on my code for several weeks, I would like to know why sometimes my "red cubes" stay stuck indefinitely in the same place despite the distance calculation between the leg (cube) and the point initial, more especially as even with checks and methods which makes it possible to force to replace the leg (cube) towards the initial position does not work all the time.

    I've already tried to solve the problem with my Unity specialist programming teacher, I've tried many forums, and even chatgpt, but nothing helped and couldn't find the solution to this problem.

    https://cdn.discordapp.com/attachme...50427432/0e6118b5c8351e00b49602ec1d63e237.mp4
     
  2. maxime66410

    maxime66410

    Joined:
    Mar 16, 2018
    Posts:
    19
    Found, there is a calculation error in my code, I am calculating in world when I have to calculate locally.

    Delete the task present in the script, it will be useless and it is not done in the right way.

    Code (CSharp):
    1.       public void FixDistanceLeg()
    2.       {
    3.           for (int i = 0; i < singleLegs.Count; i++)
    4.           {
    5.               var distanceIKtoLegLocal = Vector3.Distance(singleLegs[i].transform.localPosition, singleLegsIK[i].transform.localPosition);
    6.  
    7.               if (distanceIKtoLegLocal > distanceToFixStep)
    8.               {
    9.                   //singleLegs[i].transform.position = singleLegsIK[i].transform.position;
    10.                   singleLegs[i].transform.position = Vector3.Lerp(singleLegs[i].transform.position, singleLegsIK[i].transform.position, Time.deltaTime * speedSyncLegs);
    11.                   singleLegsTarget[i] = singleLegsIK[i].transform.position;
    12.                   singleLocalTarget[i] = singleLegsIK[i].transform.position;
    13.               }
    14.           }
    15.       }