鱼线性能优化
This commit is contained in:
@@ -8,6 +8,9 @@ public class Rope : MonoBehaviour
|
||||
[Header("Anchors")] [SerializeField] public Rigidbody startAnchor;
|
||||
[SerializeField] public Rigidbody endAnchor;
|
||||
|
||||
/// <summary>鱼线宽度倍数</summary>
|
||||
public int LineMultiple = 1;
|
||||
|
||||
[Header("Physics (Dynamic Nodes, Fixed Segment Len)")] [SerializeField, Min(0.01f), Tooltip("物理每段固定长度(越小越细致越耗)")]
|
||||
private float physicsSegmentLen = 0.15f;
|
||||
|
||||
@@ -65,7 +68,7 @@ public class Rope : MonoBehaviour
|
||||
[SerializeField, Tooltip("是否使用 Catmull-Rom 平滑(开启更顺,但更耗)")]
|
||||
private bool smooth = true;
|
||||
|
||||
[SerializeField, Min(0.0001f)] private float lineWidth = 0.002f;
|
||||
[SerializeField, Min(0.0001f)] private float lineWidth = 0.001f;
|
||||
|
||||
[Header("Air Drag (Stable)")] [SerializeField, Range(0f, 5f), Tooltip("空气阻力(Y向),指数衰减,越大越不飘")]
|
||||
private float airDrag = 0.9f;
|
||||
@@ -75,14 +78,14 @@ public class Rope : MonoBehaviour
|
||||
|
||||
private LineRenderer _lineRenderer;
|
||||
|
||||
// physics (注意:数组固定为 maxPhysicsNodes,physicsNodes 表示有效长度)
|
||||
// physics
|
||||
private int _physicsNodes;
|
||||
private Vector3[] _pCurr;
|
||||
private Vector3[] _pPrev;
|
||||
|
||||
// render
|
||||
// render (一次性分配到最大,后续不再 new)
|
||||
private Vector3[] _rPoints;
|
||||
private int _rCountCached = -1;
|
||||
private int _rCapacity;
|
||||
|
||||
private Vector3 _gravity;
|
||||
|
||||
@@ -97,17 +100,47 @@ public class Rope : MonoBehaviour
|
||||
// node stability
|
||||
private int _lastDesiredNodes = 0;
|
||||
|
||||
// caches
|
||||
private Transform _startTr;
|
||||
private Transform _endTr;
|
||||
|
||||
// precomputed
|
||||
private float _dt;
|
||||
private float _dt2;
|
||||
private float _kY;
|
||||
private float _kXZ;
|
||||
|
||||
private FRod _rod;
|
||||
public void Init(FRod rod) => _rod = rod;
|
||||
|
||||
// Catmull t caches(只缓存 idle/moving 两档,减少每帧重复乘法)
|
||||
private struct TCaches
|
||||
{
|
||||
public float[] t;
|
||||
public float[] t2;
|
||||
public float[] t3;
|
||||
}
|
||||
private TCaches _tIdle;
|
||||
private TCaches _tMoving;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_lineRenderer = GetComponent<LineRenderer>();
|
||||
_gravity = new Vector3(0f, -gravityStrength, 0f);
|
||||
|
||||
_startTr = startAnchor ? startAnchor.transform : null;
|
||||
_endTr = endAnchor ? endAnchor.transform : null;
|
||||
|
||||
InitLengthSystem();
|
||||
AllocateAndInitNodes();
|
||||
RebuildRenderBufferIfNeeded(renderSubdivisionsIdle);
|
||||
|
||||
// ✅ 渲染点一次性分配到最大: (maxNodes-1)*idle + 1
|
||||
int maxSubdiv = Mathf.Max(1, renderSubdivisionsIdle);
|
||||
_rCapacity = (maxPhysicsNodes - 1) * maxSubdiv + 1;
|
||||
_rPoints = new Vector3[_rCapacity];
|
||||
|
||||
BuildTCaches(renderSubdivisionsIdle, ref _tIdle);
|
||||
BuildTCaches(renderSubdivisionsMoving, ref _tMoving);
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
@@ -127,7 +160,6 @@ public class Rope : MonoBehaviour
|
||||
|
||||
headMinLen = Mathf.Max(headMinLen, 0.0001f);
|
||||
nodeHysteresis = Mathf.Max(0f, nodeHysteresis);
|
||||
|
||||
}
|
||||
|
||||
private void InitLengthSystem()
|
||||
@@ -141,7 +173,6 @@ public class Rope : MonoBehaviour
|
||||
{
|
||||
_physicsNodes = Mathf.Clamp(ComputeDesiredNodesStable(_currentLength), 2, maxPhysicsNodes);
|
||||
|
||||
// ✅ 永久分配到最大,运行时不再 new,避免GC卡顿
|
||||
_pCurr = new Vector3[maxPhysicsNodes];
|
||||
_pPrev = new Vector3[maxPhysicsNodes];
|
||||
|
||||
@@ -181,10 +212,7 @@ public class Rope : MonoBehaviour
|
||||
if (desired == _lastDesiredNodes)
|
||||
return desired;
|
||||
|
||||
// 边界(lastDesiredNodes 对应的长度临界)
|
||||
float boundary = (_lastDesiredNodes - 1) * physicsSegmentLen;
|
||||
|
||||
// 在临界附近就不切换,避免来回跳
|
||||
if (Mathf.Abs(lengthMeters - boundary) < nodeHysteresis)
|
||||
return _lastDesiredNodes;
|
||||
|
||||
@@ -192,11 +220,7 @@ public class Rope : MonoBehaviour
|
||||
return desired;
|
||||
}
|
||||
|
||||
public void SetTargetLength(float lengthMeters)
|
||||
{
|
||||
_targetLength = Mathf.Max(0f, lengthMeters);
|
||||
}
|
||||
|
||||
public void SetTargetLength(float lengthMeters) => _targetLength = Mathf.Max(0f, lengthMeters);
|
||||
public float GetCurrentLength() => _currentLength;
|
||||
public float GetTargetLength() => _targetLength;
|
||||
|
||||
@@ -204,29 +228,36 @@ public class Rope : MonoBehaviour
|
||||
{
|
||||
if (!startAnchor || !endAnchor) return;
|
||||
|
||||
// cache dt
|
||||
_dt = Time.fixedDeltaTime;
|
||||
if (_dt < 1e-6f) _dt = 1e-6f;
|
||||
_dt2 = _dt * _dt;
|
||||
|
||||
// gravity
|
||||
_gravity.y = -gravityStrength;
|
||||
|
||||
// drag caches(exp 比较贵,但这里每 FixedUpdate 一次,OK)
|
||||
_kY = Mathf.Exp(-airDrag * _dt);
|
||||
_kXZ = Mathf.Exp(-airDragXZ * _dt);
|
||||
|
||||
UpdateLengthSmooth();
|
||||
UpdateNodesFromLength(); // ✅ 一次性增减,无GC
|
||||
UpdateNodesFromLength();
|
||||
UpdateHeadRestLenFromCurrentLength();
|
||||
|
||||
// simulate
|
||||
Simulate();
|
||||
Simulate_VerletFast();
|
||||
|
||||
// 确保端点正确后再迭代
|
||||
// anchors
|
||||
LockAnchorsHard();
|
||||
|
||||
// constraints
|
||||
for (int it = 0; it < iterations; it++)
|
||||
SolveDistanceConstraints_HeadOnly();
|
||||
SolveDistanceConstraints_HeadOnly_Fast();
|
||||
|
||||
// 迭代后锁一次足够
|
||||
LockAnchorsHard();
|
||||
|
||||
if (constrainToGround)
|
||||
ConstrainToGround();
|
||||
|
||||
// 约束后再锁一次(保险)
|
||||
LockAnchorsHard();
|
||||
}
|
||||
|
||||
@@ -234,23 +265,23 @@ public class Rope : MonoBehaviour
|
||||
{
|
||||
if (!startAnchor || !endAnchor || _pCurr == null || _physicsNodes < 2) return;
|
||||
|
||||
// 端点“跟手”:渲染前强行同步 transform,消除慢一拍
|
||||
int last = _physicsNodes - 1;
|
||||
Vector3 s = startAnchor.transform.position;
|
||||
Vector3 e = endAnchor.transform.position;
|
||||
|
||||
// 用缓存 transform,避免多次属性链
|
||||
Vector3 s = _startTr.position;
|
||||
Vector3 e = _endTr.position;
|
||||
|
||||
_pCurr[0] = s;
|
||||
_pPrev[0] = s;
|
||||
_pCurr[last] = e;
|
||||
_pPrev[last] = e;
|
||||
|
||||
DrawHighResLine();
|
||||
DrawHighResLine_Fast();
|
||||
}
|
||||
|
||||
private void UpdateLengthSmooth()
|
||||
{
|
||||
float minFeasible = 0.01f;
|
||||
|
||||
float desired = Mathf.Max(_targetLength, minFeasible);
|
||||
|
||||
_currentLength = Mathf.SmoothDamp(
|
||||
@@ -269,22 +300,12 @@ public class Rope : MonoBehaviour
|
||||
desired = Mathf.Clamp(desired, 2, maxPhysicsNodes);
|
||||
if (desired == _physicsNodes) return;
|
||||
|
||||
if (desired > _physicsNodes)
|
||||
{
|
||||
AddNodesAtStart(desired - _physicsNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveNodesAtStart(_physicsNodes - desired);
|
||||
}
|
||||
if (desired > _physicsNodes) AddNodesAtStart(desired - _physicsNodes);
|
||||
else RemoveNodesAtStart(_physicsNodes - desired);
|
||||
|
||||
_physicsNodes = desired;
|
||||
|
||||
// 渲染缓存按最大 subdiv 预留即可
|
||||
RebuildRenderBufferIfNeeded(renderSubdivisionsIdle);
|
||||
}
|
||||
|
||||
|
||||
private void AddNodesAtStart(int addCount)
|
||||
{
|
||||
if (addCount <= 0) return;
|
||||
@@ -294,35 +315,30 @@ public class Rope : MonoBehaviour
|
||||
addCount = newCount - oldCount;
|
||||
if (addCount <= 0) return;
|
||||
|
||||
// 把 [1..oldCount-1] 整体往后挪 addCount,给 [1..addCount] 腾位置
|
||||
// oldCount-1 个元素(包含尾端锚点位,尾端后面会被锁)
|
||||
Array.Copy(_pCurr, 1, _pCurr, 1 + addCount, oldCount - 1);
|
||||
Array.Copy(_pPrev, 1, _pPrev, 1 + addCount, oldCount - 1);
|
||||
|
||||
Vector3 s = startAnchor.position;
|
||||
Vector3 s = _startTr ? _startTr.position : startAnchor.position;
|
||||
|
||||
// 方向:用“挪完后的第一个动态点”指向来估计
|
||||
Vector3 dir = Vector3.down;
|
||||
int firstOld = 1 + addCount;
|
||||
if (firstOld < 1 + addCount + (oldCount - 2) && oldCount >= 2)
|
||||
{
|
||||
Vector3 toOld1 = (_pCurr[firstOld] - s);
|
||||
if (toOld1.sqrMagnitude > 1e-6f) dir = toOld1.normalized;
|
||||
}
|
||||
|
||||
// 继承速度
|
||||
float dt = Time.fixedDeltaTime;
|
||||
Vector3 inheritVel = Vector3.zero;
|
||||
if (oldCount >= 2 && firstOld < maxPhysicsNodes)
|
||||
{
|
||||
inheritVel = (_pCurr[firstOld] - _pPrev[firstOld]) / Mathf.Max(dt, 1e-6f);
|
||||
Vector3 toOld1 = (_pCurr[firstOld] - s);
|
||||
float sq = toOld1.sqrMagnitude;
|
||||
if (sq > 1e-6f) dir = toOld1 / Mathf.Sqrt(sq);
|
||||
}
|
||||
|
||||
// inherit displacement (Verlet)
|
||||
Vector3 inheritDisp = Vector3.zero;
|
||||
if (oldCount >= 2 && firstOld < maxPhysicsNodes)
|
||||
inheritDisp = (_pCurr[firstOld] - _pPrev[firstOld]);
|
||||
|
||||
for (int k = 1; k <= addCount; k++)
|
||||
{
|
||||
Vector3 pos = s + dir * (physicsSegmentLen * k);
|
||||
_pCurr[k] = pos;
|
||||
_pPrev[k] = pos - inheritVel * dt;
|
||||
_pPrev[k] = pos - inheritDisp; // 保持动感
|
||||
}
|
||||
|
||||
LockAnchorsHard();
|
||||
@@ -337,8 +353,6 @@ public class Rope : MonoBehaviour
|
||||
removeCount = oldCount - newCount;
|
||||
if (removeCount <= 0) return;
|
||||
|
||||
// 把 [1+removeCount .. 1+removeCount+(newCount-2)-1] 挪到 [1..newCount-2]
|
||||
// 中间动态节点数量 = newCount-2
|
||||
Array.Copy(_pCurr, 1 + removeCount, _pCurr, 1, newCount - 2);
|
||||
Array.Copy(_pPrev, 1 + removeCount, _pPrev, 1, newCount - 2);
|
||||
|
||||
@@ -354,29 +368,27 @@ public class Rope : MonoBehaviour
|
||||
_headRestLen = Mathf.Clamp(_headRestLen, headMinLen, physicsSegmentLen * 1.5f);
|
||||
}
|
||||
|
||||
private void Simulate()
|
||||
/// <summary>
|
||||
/// ✅ 更快的 Verlet:去掉 /dt 和 *dt 抵消的无效计算
|
||||
/// </summary>
|
||||
private void Simulate_VerletFast()
|
||||
{
|
||||
float dt = Time.fixedDeltaTime;
|
||||
float invDt = 1f / Mathf.Max(dt, 1e-6f);
|
||||
|
||||
// 指数衰减:更稳定好调
|
||||
float kY = Mathf.Exp(-airDrag * dt);
|
||||
float kXZ = Mathf.Exp(-airDragXZ * dt);
|
||||
|
||||
// displacement = curr - prev
|
||||
// next = curr + displacement*drag*dampen + gravity*dt^2
|
||||
for (int i = 1; i < _physicsNodes - 1; i++)
|
||||
{
|
||||
Vector3 vel = (_pCurr[i] - _pPrev[i]) * invDt;
|
||||
Vector3 disp = _pCurr[i] - _pPrev[i];
|
||||
|
||||
vel.x *= kXZ;
|
||||
vel.z *= kXZ;
|
||||
vel.y *= kY;
|
||||
disp.x *= _kXZ;
|
||||
disp.z *= _kXZ;
|
||||
disp.y *= _kY;
|
||||
|
||||
vel *= velocityDampen;
|
||||
disp *= velocityDampen;
|
||||
|
||||
Vector3 next = _pCurr[i] + vel * dt;
|
||||
Vector3 next = _pCurr[i] + disp + _gravity * _dt2;
|
||||
|
||||
_pPrev[i] = _pCurr[i];
|
||||
_pCurr[i] = next + _gravity * (dt * dt);
|
||||
_pCurr[i] = next;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,21 +396,25 @@ public class Rope : MonoBehaviour
|
||||
{
|
||||
if (!startAnchor || !endAnchor || _pCurr == null || _pPrev == null || _physicsNodes < 2) return;
|
||||
|
||||
float dt = Time.fixedDeltaTime;
|
||||
Vector3 s = startAnchor.position;
|
||||
Vector3 e = endAnchor.position;
|
||||
Vector3 s = _startTr ? _startTr.position : startAnchor.position;
|
||||
Vector3 e = _endTr ? _endTr.position : endAnchor.position;
|
||||
|
||||
_pCurr[0] = s;
|
||||
_pPrev[0] = s - startAnchor.linearVelocity * dt;
|
||||
_pPrev[0] = s - startAnchor.linearVelocity * _dt;
|
||||
|
||||
int last = _physicsNodes - 1;
|
||||
_pCurr[last] = e;
|
||||
_pPrev[last] = e - endAnchor.linearVelocity * dt;
|
||||
_pPrev[last] = e - endAnchor.linearVelocity * _dt;
|
||||
}
|
||||
|
||||
private void SolveDistanceConstraints_HeadOnly()
|
||||
/// <summary>
|
||||
/// ✅ 约束:减少临时变量、用 sqrMagnitude + invDist
|
||||
/// </summary>
|
||||
private void SolveDistanceConstraints_HeadOnly_Fast()
|
||||
{
|
||||
for (int i = 0; i < _physicsNodes - 1; i++)
|
||||
int last = _physicsNodes - 1;
|
||||
|
||||
for (int i = 0; i < last; i++)
|
||||
{
|
||||
float rest = (i == 0) ? _headRestLen : physicsSegmentLen;
|
||||
|
||||
@@ -406,131 +422,151 @@ public class Rope : MonoBehaviour
|
||||
Vector3 b = _pCurr[i + 1];
|
||||
|
||||
Vector3 delta = b - a;
|
||||
float dist = delta.magnitude;
|
||||
if (dist < 1e-6f) continue;
|
||||
float sq = delta.sqrMagnitude;
|
||||
if (sq < 1e-12f) continue;
|
||||
|
||||
float diff = (dist - rest) / dist;
|
||||
Vector3 corr = delta * diff * stiffness;
|
||||
float dist = Mathf.Sqrt(sq);
|
||||
float diff = (dist - rest) / dist; // = 1 - rest/dist
|
||||
Vector3 corr = delta * (diff * stiffness);
|
||||
|
||||
if (i != 0)
|
||||
_pCurr[i] += corr * 0.5f;
|
||||
|
||||
if (i + 1 != _physicsNodes - 1)
|
||||
_pCurr[i + 1] -= corr * 0.5f;
|
||||
// i==0 锚点固定;last 锚点固定
|
||||
if (i != 0) _pCurr[i] = a + corr * 0.5f;
|
||||
if (i + 1 != last) _pCurr[i + 1] = b - corr * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
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 (constrainToGround && groundMask != 0)
|
||||
if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask,
|
||||
QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
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;
|
||||
}
|
||||
float minY = hit.point.y + groundRadius;
|
||||
if (p.y < minY) p.y = minY;
|
||||
}
|
||||
|
||||
_pCurr[i] = p;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawHighResLine()
|
||||
private void DrawHighResLine_Fast()
|
||||
{
|
||||
if (_pCurr == null || _physicsNodes < 2) return;
|
||||
|
||||
_lineRenderer.startWidth = lineWidth;
|
||||
_lineRenderer.endWidth = lineWidth;
|
||||
float w = lineWidth * LineMultiple;
|
||||
_lineRenderer.startWidth = w;
|
||||
_lineRenderer.endWidth = w;
|
||||
|
||||
if (!smooth)
|
||||
{
|
||||
_lineRenderer.positionCount = _physicsNodes;
|
||||
// 只传有效段
|
||||
// LineRenderer.SetPositions 会读数组的前 positionCount 个
|
||||
_lineRenderer.SetPositions(_pCurr);
|
||||
return;
|
||||
}
|
||||
|
||||
// 动态降 subdiv:甩动时降低点数,减少CPU开销
|
||||
int subdiv = PickRenderSubdivisions();
|
||||
RebuildRenderBufferIfNeeded(subdiv);
|
||||
int subdiv = PickRenderSubdivisions_Fast();
|
||||
TCaches tc = (subdiv == renderSubdivisionsMoving) ? _tMoving : _tIdle;
|
||||
|
||||
int needed = (_physicsNodes - 1) * subdiv + 1;
|
||||
if (needed > _rCapacity)
|
||||
{
|
||||
// 理论上不该发生(_rCapacity 用 maxNodes & idle 分配)
|
||||
// 保险扩容一次
|
||||
_rCapacity = needed;
|
||||
_rPoints = new Vector3[_rCapacity];
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
int last = _physicsNodes - 1;
|
||||
|
||||
for (int seg = 0; seg < _physicsNodes - 1; seg++)
|
||||
for (int seg = 0; seg < last; seg++)
|
||||
{
|
||||
Vector3 p0 = _pCurr[Mathf.Max(seg - 1, 0)];
|
||||
Vector3 p1 = _pCurr[seg];
|
||||
Vector3 p2 = _pCurr[seg + 1];
|
||||
Vector3 p3 = _pCurr[Mathf.Min(seg + 2, _physicsNodes - 1)];
|
||||
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;
|
||||
|
||||
Vector3 p0 = _pCurr[i0];
|
||||
Vector3 p1 = _pCurr[i1];
|
||||
Vector3 p2 = _pCurr[i2];
|
||||
Vector3 p3 = _pCurr[i3];
|
||||
|
||||
for (int s = 0; s < subdiv; s++)
|
||||
{
|
||||
float t = s / (float)subdiv;
|
||||
Vector3 pt = CatmullRom_XZ_LinearY(p0, p1, p2, p3, t);
|
||||
_rPoints[idx++] = pt;
|
||||
float t = tc.t[s];
|
||||
float t2 = tc.t2[s];
|
||||
float t3 = tc.t3[s];
|
||||
|
||||
// inline CatmullRom(少一次函数调用)
|
||||
Vector3 cr =
|
||||
0.5f * (
|
||||
(2f * p1) +
|
||||
(-p0 + p2) * t +
|
||||
(2f * p0 - 5f * p1 + 4f * p2 - p3) * t2 +
|
||||
(-p0 + 3f * p1 - 3f * p2 + p3) * t3
|
||||
);
|
||||
|
||||
// Linear Y
|
||||
cr.y = p1.y + (p2.y - p1.y) * t;
|
||||
|
||||
_rPoints[idx++] = cr;
|
||||
}
|
||||
}
|
||||
|
||||
_rPoints[idx++] = _pCurr[_physicsNodes - 1];
|
||||
_rPoints[idx++] = _pCurr[last];
|
||||
|
||||
_lineRenderer.positionCount = idx;
|
||||
_lineRenderer.SetPositions(_rPoints);
|
||||
}
|
||||
|
||||
private int PickRenderSubdivisions()
|
||||
/// <summary>
|
||||
/// ✅ 用 sqrMagnitude 比较阈值,避免 sqrt
|
||||
/// </summary>
|
||||
private int PickRenderSubdivisions_Fast()
|
||||
{
|
||||
int idle = Mathf.Max(1, renderSubdivisionsIdle);
|
||||
int moving = Mathf.Max(1, renderSubdivisionsMoving);
|
||||
|
||||
// 计算平均速度(用 Verlet 差分)
|
||||
float dt = Time.fixedDeltaTime;
|
||||
float invDt = 1f / Mathf.Max(dt, 1e-6f);
|
||||
float thr = movingSpeedThreshold;
|
||||
float thrSq = (thr * _dt) * (thr * _dt); // 因为我们用 disp = curr-prev(单位是米/step),所以阈值要乘 dt
|
||||
|
||||
float sum = 0f;
|
||||
float sumSq = 0f;
|
||||
int count = Mathf.Max(1, _physicsNodes - 2);
|
||||
|
||||
for (int i = 1; i < _physicsNodes - 1; i++)
|
||||
sum += (_pCurr[i] - _pPrev[i]).magnitude * invDt;
|
||||
|
||||
float avgSpeed = sum / count;
|
||||
|
||||
return (avgSpeed > movingSpeedThreshold) ? moving : idle;
|
||||
}
|
||||
|
||||
private void RebuildRenderBufferIfNeeded(int subdiv)
|
||||
{
|
||||
int targetCount = (_physicsNodes - 1) * subdiv + 1;
|
||||
if (_rPoints == null || _rCountCached != targetCount)
|
||||
{
|
||||
_rPoints = new Vector3[targetCount];
|
||||
_rCountCached = targetCount;
|
||||
Vector3 disp = _pCurr[i] - _pPrev[i];
|
||||
sumSq += disp.sqrMagnitude;
|
||||
}
|
||||
|
||||
float avgSq = sumSq / count;
|
||||
|
||||
return (avgSq > thrSq) ? moving : idle;
|
||||
}
|
||||
|
||||
private static Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
|
||||
private static void BuildTCaches(int subdiv, ref TCaches caches)
|
||||
{
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
subdiv = Mathf.Max(1, subdiv);
|
||||
caches.t = new float[subdiv];
|
||||
caches.t2 = new float[subdiv];
|
||||
caches.t3 = new float[subdiv];
|
||||
|
||||
return 0.5f * (
|
||||
(2f * p1) +
|
||||
(-p0 + p2) * t +
|
||||
(2f * p0 - 5f * p1 + 4f * p2 - p3) * t2 +
|
||||
(-p0 + 3f * p1 - 3f * p2 + p3) * t3
|
||||
);
|
||||
}
|
||||
|
||||
private static Vector3 CatmullRom_XZ_LinearY(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
|
||||
{
|
||||
Vector3 cr = CatmullRom(p0, p1, p2, p3, t);
|
||||
cr.y = Mathf.Lerp(p1.y, p2.y, t);
|
||||
return cr;
|
||||
float inv = 1f / subdiv;
|
||||
for (int s = 0; s < subdiv; s++)
|
||||
{
|
||||
float t = s * inv;
|
||||
float t2 = t * t;
|
||||
caches.t[s] = t;
|
||||
caches.t2[s] = t2;
|
||||
caches.t3[s] = t2 * t;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
|
||||
Reference in New Issue
Block a user