英文:
Procedural animation for a crab with Rigging animation & Inverse kinematic
问题
我在Unity上工作,我必须完全更改我的脚本,以实现逆运动学的过程动画,实际上,为了向您解释,我有一只有8条腿的螃蟹,每条腿都有4个枢轴点,我想要的是当螃蟹移动时,腿也要移动,通过过程动画来实现。
我已经创建了一个脚本来完成所有这些,但是有时点会保持在原地,无法重置它,即使进行检查,无论在计算中、计算之外,甚至通过强制使用任务等方式。
所以我想知道这里是否有人可以帮助我修复这个错误,或者从一个良好的基础开始完全重新做一切。
这是我的完整脚本。
// ReSharper disable once InvalidXmlDocComment
/**
* Original Script by: Sanchez Maxime (@Maxime66410)
* @version 1.0.0
* @autor Sanchez Maxime (@Maxime66410)
*
* Last modification: 09/06/2023
* File description: This script is used to procedurally animate the crab
*/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Serialization;
// ReSharper disable once CheckNamespace
public class CrabPatteCombine : MonoBehaviour
{
[FormerlySerializedAs("SingleLegs")] [Header("Legs GameObject"), Space(10)]
public List<GameObject> singleLegs = new List<GameObject>(); // Paw GameObject
[FormerlySerializedAs("SingleLegsIK")] public List<GameObject> singleLegsIK = new List<GameObject>(); // IK's GameObject
[FormerlySerializedAs("SingleLegsTarget")] [Header("Vector3"), Space(10)]
public List<Vector3> singleLegsTarget = new List<Vector3>(); // Leg position
[FormerlySerializedAs("SingleLocalTarget")] public List<Vector3> singleLocalTarget = new List<Vector3>(); // Position of the IK
// ...(省略了一部分脚本内容)...
}
这是我的问题:
如您所见,有时红色立方体会保持不动。
由于我在我的代码上遇到了一个小问题已经几周了,我想知道为什么有时我的“红色立方体”尽管在腿(立方体)和初始点之间进行了距离计算,但仍然永远停在原地,
尤其是即使使用了检查和强制方法来强制将腿(立方体)重新放置到初始位置,也不总是有效。
我已经尝试过与我的Unity专业编程老师一起解决这个问题,我在许多论坛上尝试过,甚至与chatgpt聊天,但没有帮助,找不到解决这个问题的方法。
英文:
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.
// ReSharper disable once InvalidXmlDocComment
/**
* Original Script by: Sanchez Maxime (@Maxime66410)
* @version 1.0.0
* @autor Sanchez Maxime (@Maxime66410)
*
* Last modification: 09/06/2023
* File description: This script is used to procedurally animate the crab
*/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Serialization;
// ReSharper disable once CheckNamespace
public class CrabPatteCombine : MonoBehaviour
{
[FormerlySerializedAs("SingleLegs")] [Header("Legs GameObject"), Space(10)]
public List<GameObject> singleLegs = new List<GameObject>(); // Paw GameObject
[FormerlySerializedAs("SingleLegsIK")] public List<GameObject> singleLegsIK = new List<GameObject>(); // IK's GameObject
[FormerlySerializedAs("SingleLegsTarget")] [Header("Vector3"), Space(10)]
public List<Vector3> singleLegsTarget = new List<Vector3>(); // Leg position
[FormerlySerializedAs("SingleLocalTarget")] public List<Vector3> singleLocalTarget = new List<Vector3>(); // Position of the IK
[Header("Legs / Step"), Space(10)]
[FormerlySerializedAs("SpeedSyncLegs")] public float speedSyncLegs = 10f; // paw speed
[FormerlySerializedAs("DistanceToPoint")] public float distanceToPoint = 0.075f; // Distance between paw and target
[FormerlySerializedAs("StepHeight")] public float stepHeight = 0.5f; // step height
[FormerlySerializedAs("StepHeightCurve")] public AnimationCurve stepHeightCurve; // Step height curve
[FormerlySerializedAs("OffsetStep")] public Vector3 offsetStep = Vector3.zero; // Step height offset
[FormerlySerializedAs("_distanceStep")] [SerializeField] private List<float> distanceStep = new List<float>(); // Distance between paw and target
[FormerlySerializedAs("_distanceToLegFromIK")] [SerializeField] private List<float> distanceToLegFromIK = new List<float>(); // Distance between paw and target
[FormerlySerializedAs("DistanceToStopSyncLeg")] public float distanceToStopSyncLeg = 0.01f; // Distance between paw and target to stop synchronization
[FormerlySerializedAs("MaxDistanceToSyncLeg")] public float maxDistanceToSyncLeg = 1f; // Maximum distance between paw and target to synchronize the paw
[FormerlySerializedAs("MaxDistanceToSyncLeg")] public float maxDistanceDebug = 0.2f; // Maximum distance between paw and target to synchronize the paw (Debug)
[FormerlySerializedAs("RaycastDistance")] public float raycastDistance = 0.3f; // Raycast distance
[FormerlySerializedAs("LayerMaskGround")] public LayerMask layerMaskGround; // LayerMask
public void Awake()
{
// Automatic initialization of the lists
for (int i = 0; i < singleLegs.Count; i++)
{
distanceStep.Add(0f);
distanceToLegFromIK.Add(0f);
singleLegsTarget.Add(Vector3.zero);
singleLocalTarget.Add(Vector3.zero);
}
}
public void Start()
{
GetSurfaceOnStart();
#pragma warning disable CS4014
CheckDistanceLeg();
#pragma warning restore CS4014
}
// Get crab area at start Position and rotation
public void GetSurfaceOnStart()
{
// If SingleLegs is not empty
if (singleLegs.Count > 0)
{
for (int i = 0; i < singleLegs.Count; i++)
{
singleLocalTarget[i] = singleLegsIK[i].transform.position; // Get IK paw position
// Get the surface of the starting crab Position and rotation
RaycastHit hit;
if (Physics.Raycast(transform.position, Vector3.down, out hit, 0.03f, layerMaskGround))
{
singleLegs[i].transform.position = hit.point;
singleLegsTarget[i] = hit.point;
Transform transform1 = transform;
transform1.rotation = Quaternion.FromToRotation(transform1.up, hit.normal) * (transform).rotation;
}
}
}
}
public void FixedUpdate()
{
CheckDistance();
UpdatePosition();
}
// Check the distance between the leg and the target
public void CheckDistance()
{
// If SingleLegs is not empty
if(singleLegs.Count > 0)
{
// Check the distance between the leg and the target
for (int i = 0; i < singleLegs.Count; i++)
{
RaycastHit hit;
if (Physics.Raycast(singleLocalTarget[i], Vector3.down, out hit, raycastDistance, layerMaskGround))
{
distanceStep[i] = Vector3.Distance(singleLegs[i].transform.position, hit.point);
distanceToLegFromIK[i] = Vector3.Distance(singleLegs[i].transform.localPosition, singleLegsIK[i].transform.localPosition);
// If the remaining distance is really too much greater than the target distance, reset the leg position (Fix Bug)
if (distanceToLegFromIK[i] > maxDistanceToSyncLeg)
{
singleLegs[i].transform.position = singleLegsIK[i].transform.position;
singleLegsTarget[i] = singleLegsIK[i].transform.position;
singleLocalTarget[i] = singleLegsIK[i].transform.position;
}
else
{
// If the distance is greater than DistanceToPoint, update the leg position
if (Vector3.Distance(singleLegs[i].transform.position, hit.point) > distanceToPoint)
{
singleLegsTarget[i] = hit.point;
}
}
}
}
}
}
// Update leg position
public void UpdatePosition()
{
if (singleLegs.Count > 0)
{
for (int i = 0; i < singleLegs.Count; i++)
{
if (singleLegs[i].transform.position != singleLegsTarget[i])
{
// Calculate the remaining distance between the current leg position and the target
float remainingDistance = Vector3.Distance(singleLegs[i].transform.position, singleLegsTarget[i]);
// Calculate total step based on remaining distance
float totalStep = distanceToPoint * stepHeightCurve.length;
// If the remaining distance is less than the total step, adjust the height of the step
if (remainingDistance < totalStep)
{
// Calculate step height using StepHeightCurve
// ReSharper disable once LocalVariableHidesMember
float stepHeight = stepHeightCurve.Evaluate(1f - (remainingDistance / totalStep)) * this.stepHeight;
// Move paw to target with height adjustment
singleLegs[i].transform.position = Vector3.Lerp(singleLegs[i].transform.position, singleLegsTarget[i], Time.deltaTime * speedSyncLegs) + (offsetStep * stepHeight);
// Adjust paw height to simulate lifting motion
singleLegs[i].transform.position += Vector3.up * stepHeight;
// If the remaining distance is less than a small threshold value, reset the position of the paw
if (remainingDistance < distanceToStopSyncLeg)
{
singleLegs[i].transform.position = singleLegsTarget[i];
}
// If the remaining distance is really too much greater than the distance to the target, reset the position of the paw
if (remainingDistance > maxDistanceToSyncLeg)
{
singleLegs[i].transform.position = singleLegsIK[i].transform.position;
}
}
else
{
// Move paw to target without height adjustment
singleLegs[i].transform.position = Vector3.Lerp(singleLegs[i].transform.position, singleLegsTarget[i], Time.deltaTime * speedSyncLegs);
FixDistanceLeg();
}
}
else
{
// If the remaining distance is really too much greater than the distance to the target, reset the position of the paw
float remainingDistance = Vector3.Distance(singleLegs[i].transform.position, singleLegsIK[i].transform.position);
// If the remaining distance is really too much greater than the distance to the target, reset the position of the paw
if (remainingDistance > maxDistanceToSyncLeg)
{
singleLegs[i].transform.position = Vector3.Lerp(singleLegs[i].transform.position, singleLegsIK[i].transform.position, Time.deltaTime * speedSyncLegs);
}
}
// Get the ground surface below the leg and adjust the local target
RaycastHit hit;
if (Physics.Raycast(singleLegsIK[i].transform.position, Vector3.down, out hit, raycastDistance, layerMaskGround))
{
singleLocalTarget[i] = Vector3.Lerp(singleLocalTarget[i], new Vector3(singleLegsIK[i].transform.position.x, hit.point.y, singleLegsIK[i].transform.position.z), Time.deltaTime * speedSyncLegs);
}
FixDistanceLeg();
}
}
// SingleLocalTarget = SingleLegsIK.transform.position; // OLD
}
// If the remaining distance is really too much greater than the target distance, reset the leg position (Fix Bug)
public void FixDistanceLeg()
{
for (int i = 0; i < singleLegs.Count; i++)
{
if (distanceToLegFromIK[i] > maxDistanceToSyncLeg)
{
singleLegs[i].transform.position = singleLegsIK[i].transform.position;
singleLegsTarget[i] = singleLegsIK[i].transform.position;
singleLocalTarget[i] = singleLegsIK[i].transform.position;
}
}
}
// ReSharper disable once FunctionRecursiveOnAllPaths
public async Task CheckDistanceLeg()
{
for (int i = 0; i < singleLegs.Count; i++)
{
// If the distance between the paw and the target is greater than the maximum distance, reset the position of the paw
float distance = Vector3.Distance(singleLegs[i].transform.position, singleLegsIK[i].transform.position);
//Debug.Log($"Distance to Leg + {singleLegsIK[i].name} = {distance}");
if(distance > maxDistanceDebug)
{
singleLegs[i].transform.position = singleLegsIK[i].transform.position;
singleLegsTarget[i] = singleLegsIK[i].transform.position;
singleLocalTarget[i] = singleLegsIK[i].transform.position;
}
}
await Task.Delay(TimeSpan.FromSeconds(1));
await CheckDistanceLeg();
}
// Draw a line between the paw and the target
public void OnDrawGizmos()
{
if (singleLocalTarget.Count > 0)
{
for (int i = 0; i < singleLegs.Count; i++)
{
if (singleLocalTarget[i] != Vector3.zero)
{
Gizmos.color = Color.magenta;
Gizmos.DrawLine(singleLegs[i].transform.position, singleLocalTarget[i]);
}
if(singleLocalTarget[i] != Vector3.zero)
{
Gizmos.color = Color.red;
Gizmos.DrawLine(singleLocalTarget[i], singleLocalTarget[i] + Vector3.down * raycastDistance);
}
}
}
}
}
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.
答案1
得分: 0
在我的代码中发现了一个计算错误,我是在全局计算,但我应该在本地计算。
删除脚本中的任务,它是无用的,而且做法不正确。
public void FixDistanceLeg()
{
for (int i = 0; i < singleLegs.Count; i++)
{
var distanceIKtoLegLocal = Vector3.Distance(singleLegs[i].transform.localPosition, singleLegsIK[i].transform.localPosition);
if (distanceIKtoLegLocal > distanceToFixStep)
{
//singleLegs[i].transform.position = singleLegsIK[i].transform.position;
singleLegs[i].transform.position = Vector3.Lerp(singleLegs[i].transform.position, singleLegsIK[i].transform.position, Time.deltaTime * speedSyncLegs);
singleLegsTarget[i] = singleLegsIK[i].transform.position;
singleLocalTarget[i] = singleLegsIK[i].transform.position;
}
}
}
英文:
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.
public void FixDistanceLeg()
{
for (int i = 0; i < singleLegs.Count; i++)
{
var distanceIKtoLegLocal = Vector3.Distance(singleLegs[i].transform.localPosition, singleLegsIK[i].transform.localPosition);
if (distanceIKtoLegLocal > distanceToFixStep)
{
//singleLegs[i].transform.position = singleLegsIK[i].transform.position;
singleLegs[i].transform.position = Vector3.Lerp(singleLegs[i].transform.position, singleLegsIK[i].transform.position, Time.deltaTime * speedSyncLegs);
singleLegsTarget[i] = singleLegsIK[i].transform.position;
singleLocalTarget[i] = singleLegsIK[i].transform.position;
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论