优化绳逻辑

This commit is contained in:
2026-02-26 00:05:18 +08:00
parent adbd097fd5
commit 06e5d9ae1a
2 changed files with 109 additions and 15 deletions

View File

@@ -55,6 +55,17 @@ public class Rope : MonoBehaviour
[SerializeField, Min(0f)] private float groundRadius = 0.01f;
[SerializeField, Min(0f)] private float groundCastHeight = 1.0f;
[SerializeField, Min(0.01f)] private float groundCastDistance = 2.5f;
[SerializeField, Range(1, 8), Tooltip("每隔多少个节点做一次地面检测;越大越省")]
private int groundSampleStep = 3;
[SerializeField, Tooltip("未采样的点用插值还是直接拷贝邻近采样值")]
private bool groundInterpolate = true;
[SerializeField, Range(1, 8), Tooltip("每隔多少次FixedUpdate更新一次地面约束")]
private int groundUpdateEvery = 2;
private int _groundFrameCounter;
[Header("Render (High Resolution)")] [SerializeField, Min(1), Tooltip("静止时每段物理线段插值加密数量(越大越顺,越耗)")]
private int renderSubdivisionsIdle = 6;
@@ -120,6 +131,7 @@ public class Rope : MonoBehaviour
public float[] t2;
public float[] t3;
}
private TCaches _tIdle;
private TCaches _tMoving;
@@ -256,7 +268,14 @@ public class Rope : MonoBehaviour
LockAnchorsHard();
if (constrainToGround)
ConstrainToGround();
{
_groundFrameCounter++;
if (_groundFrameCounter >= groundUpdateEvery)
{
_groundFrameCounter = 0;
ConstrainToGround();
}
}
LockAnchorsHard();
}
@@ -435,25 +454,94 @@ public class Rope : MonoBehaviour
}
}
// private void ConstrainToGround()
// {
// if (groundMask == 0) return;
//
// // RaycastHit 是 struct这里不会 GC
// for (int i = 1; i < _physicsNodes - 1; i++)
// {
// Vector3 p = _pCurr[i];
// Vector3 origin = p + Vector3.up * groundCastHeight;
//
// if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask,
// QueryTriggerInteraction.Ignore))
// {
// float minY = hit.point.y + groundRadius;
// if (p.y < minY) p.y = minY;
// }
//
// _pCurr[i] = p;
// }
// }
private void ConstrainToGround()
{
if (groundMask == 0) return;
// RaycastHit 是 struct这里不会 GC
for (int i = 1; i < _physicsNodes - 1; i++)
{
Vector3 p = _pCurr[i];
Vector3 origin = p + Vector3.up * groundCastHeight;
int last = _physicsNodes - 1;
int step = Mathf.Max(1, groundSampleStep);
if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask,
QueryTriggerInteraction.Ignore))
// 记录采样点的“最低允许Y”
// 不想分配数组就用局部变量滚动插值
int prevSampleIdx = 1;
float prevMinY = SampleMinY(_pCurr[prevSampleIdx]);
// 把采样点先处理掉
ApplyMinY(prevSampleIdx, prevMinY);
for (int i = 1 + step; i < last; i += step)
{
float nextMinY = SampleMinY(_pCurr[i]);
ApplyMinY(i, nextMinY);
if (groundInterpolate)
{
float minY = hit.point.y + groundRadius;
if (p.y < minY) p.y = minY;
// 在两个采样点之间插值 minY视觉更平滑
int a = prevSampleIdx;
int b = i;
int span = b - a;
for (int j = 1; j < span; j++)
{
int idx = a + j;
float t = j / (float)span;
float minY = Mathf.Lerp(prevMinY, nextMinY, t);
ApplyMinY(idx, minY);
}
}
else
{
// 直接用 prevMinY 填充中间点(更省)
for (int idx = prevSampleIdx + 1; idx < i; idx++)
ApplyMinY(idx, prevMinY);
}
_pCurr[i] = p;
prevSampleIdx = i;
prevMinY = nextMinY;
}
// 尾巴剩余部分用最后一个采样值填
for (int i = prevSampleIdx + 1; i < last; i++)
ApplyMinY(i, prevMinY);
}
private float SampleMinY(Vector3 p)
{
Vector3 origin = p + Vector3.up * groundCastHeight;
if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask,
QueryTriggerInteraction.Ignore))
return hit.point.y + groundRadius;
// 没命中就不抬(返回极小值)
return float.NegativeInfinity;
}
private void ApplyMinY(int i, float minY)
{
if (float.IsNegativeInfinity(minY)) return;
Vector3 p = _pCurr[i];
if (p.y < minY) p.y = minY;
_pCurr[i] = p;
}
private void DrawHighResLine_Fast()
@@ -488,10 +576,12 @@ public class Rope : MonoBehaviour
for (int seg = 0; seg < last; seg++)
{
int i0 = seg - 1; if (i0 < 0) i0 = 0;
int i0 = seg - 1;
if (i0 < 0) i0 = 0;
int i1 = seg;
int i2 = seg + 1;
int i3 = seg + 2; if (i3 > last) i3 = last;
int i3 = seg + 2;
if (i3 > last) i3 = last;
Vector3 p0 = _pCurr[i0];
Vector3 p1 = _pCurr[i1];