ADD : 1차 스트레치 테스트 스크립트 업데이트
This commit is contained in:
parent
82a1df91a7
commit
097678e572
@ -129,6 +129,22 @@ namespace RootMotion.FinalIK
|
||||
/// </summary>
|
||||
public AnimationCurve stretchCurve = new AnimationCurve();
|
||||
|
||||
[LargeHeader("Struggle (Anti-Popping)")]
|
||||
|
||||
[Tooltip("When the arm reaches near maximum extension, this feature smoothly transitions to prevent sudden 'popping' or 'snapping'. Start value represents the percentage of arm length (0.99 = 99%) where the smoothing begins. Inspired by hvr-ik's struggle mechanism.")]
|
||||
/// <summary>
|
||||
/// When the arm reaches near maximum extension, this feature smoothly transitions to prevent sudden 'popping' or 'snapping'. Start value represents the percentage of arm length where the smoothing begins.
|
||||
/// </summary>
|
||||
[Range(0.9f, 1.0f)]
|
||||
public float struggleStart = 0.99f;
|
||||
|
||||
[Tooltip("The end threshold for the struggle zone. Represents the percentage of arm length (1.04 = 104%) where the smoothing ends. The arm will smoothly approach full extension within the struggle zone (struggleStart to struggleEnd).")]
|
||||
/// <summary>
|
||||
/// The end threshold for the struggle zone. The arm will smoothly approach full extension within this range.
|
||||
/// </summary>
|
||||
[Range(1.0f, 1.2f)]
|
||||
public float struggleEnd = 1.04f;
|
||||
|
||||
/// <summary>
|
||||
/// Target position of the hand. Will be overwritten if target is assigned.
|
||||
/// </summary>
|
||||
@ -305,15 +321,15 @@ namespace RootMotion.FinalIK
|
||||
if (LOD < 1) Stretching();
|
||||
|
||||
Vector3 bendNormal = GetBendNormal(position - upperArm.solverPosition);
|
||||
|
||||
// 팔 부분만 IK 적용
|
||||
|
||||
// 팔 부분만 IK 적용 (Struggle 메커니즘 포함)
|
||||
if (hasShoulder)
|
||||
{
|
||||
VirtualBone.SolveTrigonometric(bones, 1, 2, 3, position, bendNormal, positionWeight);
|
||||
VirtualBone.SolveTrigonometric(bones, 1, 2, 3, position, bendNormal, positionWeight, struggleStart, struggleEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
VirtualBone.SolveTrigonometric(bones, 0, 1, 2, position, bendNormal, positionWeight);
|
||||
VirtualBone.SolveTrigonometric(bones, 0, 1, 2, position, bendNormal, positionWeight, struggleStart, struggleEnd);
|
||||
}
|
||||
|
||||
// 손 회전 적용
|
||||
|
||||
@ -76,6 +76,22 @@ namespace RootMotion.FinalIK {
|
||||
/// </summary>
|
||||
public AnimationCurve stretchCurve = new AnimationCurve();
|
||||
|
||||
[LargeHeader("Struggle (Anti-Popping)")]
|
||||
|
||||
[Tooltip("When the leg reaches near maximum extension, this feature smoothly transitions to prevent sudden 'popping' or 'snapping'. Start value represents the percentage of leg length (0.99 = 99%) where the smoothing begins. Inspired by hvr-ik's struggle mechanism.")]
|
||||
/// <summary>
|
||||
/// When the leg reaches near maximum extension, this feature smoothly transitions to prevent sudden 'popping' or 'snapping'. Start value represents the percentage of leg length where the smoothing begins.
|
||||
/// </summary>
|
||||
[Range(0.9f, 1.0f)]
|
||||
public float struggleStart = 0.99f;
|
||||
|
||||
[Tooltip("The end threshold for the struggle zone. Represents the percentage of leg length (1.04 = 104%) where the smoothing ends. The leg will smoothly approach full extension within the struggle zone (struggleStart to struggleEnd).")]
|
||||
/// <summary>
|
||||
/// The end threshold for the struggle zone. The leg will smoothly approach full extension within this range.
|
||||
/// </summary>
|
||||
[Range(1.0f, 1.2f)]
|
||||
public float struggleEnd = 1.04f;
|
||||
|
||||
/// <summary>
|
||||
/// Target position of the toe/foot. Will be overwritten if target is assigned.
|
||||
/// </summary>
|
||||
@ -283,8 +299,8 @@ namespace RootMotion.FinalIK {
|
||||
public void Solve(bool stretch) {
|
||||
if (stretch && LOD < 1) Stretching();
|
||||
|
||||
// Foot pass
|
||||
VirtualBone.SolveTrigonometric(bones, 0, 1, 2, footPosition, bendNormal, 1f);
|
||||
// Foot pass (with Struggle mechanism)
|
||||
VirtualBone.SolveTrigonometric(bones, 0, 1, 2, footPosition, bendNormal, 1f, struggleStart, struggleEnd);
|
||||
|
||||
// Rotate foot back to where it was before the last solving
|
||||
RotateTo(foot, footRotation);
|
||||
@ -295,10 +311,10 @@ namespace RootMotion.FinalIK {
|
||||
FixTwistRotations();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Vector3 b = Vector3.Cross(foot.solverPosition - thigh.solverPosition, toes.solverPosition - foot.solverPosition).normalized;
|
||||
|
||||
VirtualBone.SolveTrigonometric(bones, 0, 2, 3, position, b, 1f);
|
||||
VirtualBone.SolveTrigonometric(bones, 0, 2, 3, position, b, 1f, struggleStart, struggleEnd);
|
||||
|
||||
// Fix thigh twist relative to target rotation
|
||||
FixTwistRotations();
|
||||
|
||||
@ -133,23 +133,83 @@ namespace RootMotion.FinalIK {
|
||||
|
||||
// Direction of the limb in solver
|
||||
targetPosition = Vector3.Lerp(bones[third].solverPosition, targetPosition, weight);
|
||||
|
||||
|
||||
Vector3 dir = targetPosition - bones[first].solverPosition;
|
||||
|
||||
|
||||
// Distance between the first and the last transform solver positions
|
||||
float sqrMag = dir.sqrMagnitude;
|
||||
if (sqrMag == 0f) return;
|
||||
float length = Mathf.Sqrt(sqrMag);
|
||||
|
||||
|
||||
float sqrMag1 = (bones[second].solverPosition - bones[first].solverPosition).sqrMagnitude;
|
||||
float sqrMag2 = (bones[third].solverPosition - bones[second].solverPosition).sqrMagnitude;
|
||||
|
||||
|
||||
// Get the general world space bending direction
|
||||
Vector3 bendDir = Vector3.Cross(dir, bendNormal);
|
||||
|
||||
|
||||
// Get the direction to the trigonometrically solved position of the second transform
|
||||
Vector3 toBendPoint = GetDirectionToBendPoint(dir, length, bendDir, sqrMag1, sqrMag2);
|
||||
|
||||
|
||||
// Position the second transform
|
||||
Quaternion q1 = Quaternion.FromToRotation(bones[second].solverPosition - bones[first].solverPosition, toBendPoint);
|
||||
if (weight < 1f) q1 = Quaternion.Lerp(Quaternion.identity, q1, weight);
|
||||
|
||||
RotateAroundPoint(bones, first, bones[first].solverPosition, q1);
|
||||
|
||||
Quaternion q2 = Quaternion.FromToRotation(bones[third].solverPosition - bones[second].solverPosition, targetPosition - bones[second].solverPosition);
|
||||
if (weight < 1f) q2 = Quaternion.Lerp(Quaternion.identity, q2, weight);
|
||||
|
||||
RotateAroundPoint(bones, second, bones[second].solverPosition, q2);
|
||||
}
|
||||
|
||||
// Overload with struggle parameters (inspired by hvr-ik)
|
||||
/// <summary>
|
||||
/// Solve the bone chain virtually with struggle mechanism to prevent popping when near maximum extension.
|
||||
/// </summary>
|
||||
public static void SolveTrigonometric(VirtualBone[] bones, int first, int second, int third, Vector3 targetPosition, Vector3 bendNormal, float weight, float struggleStart, float struggleEnd) {
|
||||
if (weight <= 0f) return;
|
||||
|
||||
// Direction of the limb in solver
|
||||
targetPosition = Vector3.Lerp(bones[third].solverPosition, targetPosition, weight);
|
||||
|
||||
Vector3 rootPos = bones[first].solverPosition;
|
||||
float limbLength1 = Mathf.Sqrt((bones[second].solverPosition - bones[first].solverPosition).sqrMagnitude);
|
||||
float limbLength2 = Mathf.Sqrt((bones[third].solverPosition - bones[second].solverPosition).sqrMagnitude);
|
||||
float totalLength = limbLength1 + limbLength2;
|
||||
|
||||
// Apply Struggle Correction (hvr-ik style)
|
||||
float distance = Vector3.Distance(rootPos, targetPosition);
|
||||
if (distance >= totalLength * struggleStart) {
|
||||
float finalLength;
|
||||
if (struggleStart != struggleEnd) {
|
||||
// Map distance to 0-1 in the struggle zone
|
||||
float lerpAmount = Mathf.Clamp01((distance - totalLength * struggleStart) / (totalLength * struggleEnd - totalLength * struggleStart));
|
||||
// Use pow(1-x, 4) curve for smooth transition (capacitor charge curve)
|
||||
float easedAmount = 1f - Mathf.Pow(1f - lerpAmount, 4f);
|
||||
finalLength = Mathf.Lerp(totalLength * struggleStart, totalLength, easedAmount);
|
||||
} else {
|
||||
finalLength = totalLength;
|
||||
}
|
||||
|
||||
// Pull target position inward to the corrected distance
|
||||
targetPosition = rootPos + (targetPosition - rootPos).normalized * finalLength;
|
||||
}
|
||||
|
||||
// Continue with original trigonometric solving
|
||||
Vector3 dir = targetPosition - rootPos;
|
||||
float sqrMag = dir.sqrMagnitude;
|
||||
if (sqrMag == 0f) return;
|
||||
float length = Mathf.Sqrt(sqrMag);
|
||||
|
||||
float sqrMag1 = limbLength1 * limbLength1;
|
||||
float sqrMag2 = limbLength2 * limbLength2;
|
||||
|
||||
// Get the general world space bending direction
|
||||
Vector3 bendDir = Vector3.Cross(dir, bendNormal);
|
||||
|
||||
// Get the direction to the trigonometrically solved position of the second transform
|
||||
Vector3 toBendPoint = GetDirectionToBendPoint(dir, length, bendDir, sqrMag1, sqrMag2);
|
||||
|
||||
// Position the second transform
|
||||
Quaternion q1 = Quaternion.FromToRotation(bones[second].solverPosition - bones[first].solverPosition, toBendPoint);
|
||||
if (weight < 1f) q1 = Quaternion.Lerp(Quaternion.identity, q1, weight);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user