优化绳逻辑
This commit is contained in:
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user