This commit is contained in:
2026-04-14 00:14:08 +08:00
parent 9c351b3823
commit 2a5f266400

View File

@@ -42,9 +42,6 @@ public class FishingNodeRope : MonoBehaviour
[Tooltip("当长度在变化时额外把速度压掉一些防抖。0=不额外处理1=变化时几乎清速度(建议只在收线生效)")] [SerializeField, Range(0f, 1f)] [Tooltip("当长度在变化时额外把速度压掉一些防抖。0=不额外处理1=变化时几乎清速度(建议只在收线生效)")] [SerializeField, Range(0f, 1f)]
private float lengthChangeVelocityKill = 0.6f; private float lengthChangeVelocityKill = 0.6f;
[Tooltip("允许的最小松弛余量(避免目标长度刚好等于锚点距离时抖动)")] [SerializeField, Min(0f)]
private float minSlack = 0.002f;
[Header("Head Segment Clamp")] [Tooltip("第一段(起点->第1节点允许的最小长度避免收线时第一段被压到0导致数值炸")] [SerializeField, Min(0.0001f)] [Header("Head Segment Clamp")] [Tooltip("第一段(起点->第1节点允许的最小长度避免收线时第一段被压到0导致数值炸")] [SerializeField, Min(0.0001f)]
private float headMinLen = 0.01f; private float headMinLen = 0.01f;
@@ -158,16 +155,12 @@ public class FishingNodeRope : MonoBehaviour
// node stability // node stability
private int _lastDesiredNodes = 0; private int _lastDesiredNodes = 0;
// caches
private Transform _startTr;
private Transform _endTr;
// precomputed // precomputed
private float _dt; private float _dt;
private float _dt2; private float _dt2;
private float _kY; private float _kY;
private float _kXZ; private float _kXZ;
private Transform _cameraTr; private Camera _cachedCamera;
private int _visibilityCheckCounter; private int _visibilityCheckCounter;
private bool _isCulledByVisibility; private bool _isCulledByVisibility;
private int _tIdleSubdiv = -1; private int _tIdleSubdiv = -1;
@@ -192,18 +185,22 @@ public class FishingNodeRope : MonoBehaviour
private TCaches _tIdle; private TCaches _tIdle;
private TCaches _tMoving; private TCaches _tMoving;
private enum SampleConstraintMode
{
GroundMinY,
WaterSurfaceY
}
private void Awake() private void Awake()
{ {
_lineRenderer = GetComponent<LineRenderer>(); _lineRenderer = GetComponent<LineRenderer>();
_gravity = new Vector3(0f, -gravityStrength, 0f); _gravity = new Vector3(0f, -gravityStrength, 0f);
_dt = Mathf.Max(Time.fixedDeltaTime, 1e-6f);
RefreshAnchorTransforms(); _dt2 = _dt * _dt;
InitLengthSystem(); InitLengthSystem();
AllocateAndInitNodes(); AllocateAndInitNodes();
EnsureRenderCaches(); EnsureRenderCaches();
RefreshVisibilityState(true);
} }
private void OnValidate() private void OnValidate()
@@ -238,12 +235,6 @@ public class FishingNodeRope : MonoBehaviour
visibilityViewportPadding = Mathf.Clamp(visibilityViewportPadding, 0f, 0.5f); visibilityViewportPadding = Mathf.Clamp(visibilityViewportPadding, 0f, 0.5f);
} }
private void RefreshAnchorTransforms()
{
_startTr = startAnchor ? startAnchor.transform : null;
_endTr = endAnchor ? endAnchor.transform : null;
}
private bool ShouldAlwaysSimulate() private bool ShouldAlwaysSimulate()
{ {
if (!localOwnerAlwaysSimulate) if (!localOwnerAlwaysSimulate)
@@ -253,23 +244,28 @@ public class FishingNodeRope : MonoBehaviour
return owner == null || owner.IsSelf; return owner == null || owner.IsSelf;
} }
private Transform GetActiveCameraTransform() private Vector3 GetStartPosition()
{ {
Camera main = BaseCamera.Main; return startAnchor ? startAnchor.position : transform.position;
if (main) }
private Vector3 GetEndPosition()
{
return endAnchor ? endAnchor.position : transform.position;
}
private Camera GetActiveCamera()
{
if (BaseCamera.Main)
{ {
_cameraTr = main.transform; _cachedCamera = BaseCamera.Main;
return _cameraTr; return _cachedCamera;
} }
if (!_cameraTr) if (!_cachedCamera)
{ _cachedCamera = Camera.main;
Camera fallback = Camera.main;
if (fallback)
_cameraTr = fallback.transform;
}
return _cameraTr; return _cachedCamera;
} }
private static bool IsViewportPointVisible(Vector3 viewportPoint, float padding) private static bool IsViewportPointVisible(Vector3 viewportPoint, float padding)
@@ -283,18 +279,12 @@ public class FishingNodeRope : MonoBehaviour
private bool IsVisibleToMainCamera() private bool IsVisibleToMainCamera()
{ {
Transform camTr = GetActiveCameraTransform(); Camera cam = GetActiveCamera();
if (!camTr)
return true;
Camera cam = camTr.GetComponent<Camera>();
if (!cam)
cam = BaseCamera.Main ? BaseCamera.Main : Camera.main;
if (!cam) if (!cam)
return true; return true;
Vector3 start = _startTr ? _startTr.position : (startAnchor ? startAnchor.position : transform.position); Vector3 start = GetStartPosition();
Vector3 end = _endTr ? _endTr.position : (endAnchor ? endAnchor.position : transform.position); Vector3 end = GetEndPosition();
Vector3 middle = (start + end) * 0.5f; Vector3 middle = (start + end) * 0.5f;
float padding = visibilityViewportPadding; float padding = visibilityViewportPadding;
@@ -345,8 +335,8 @@ public class FishingNodeRope : MonoBehaviour
if (_physicsNodes < 2) if (_physicsNodes < 2)
return; return;
Vector3 start = _startTr ? _startTr.position : (startAnchor ? startAnchor.position : transform.position); Vector3 start = GetStartPosition();
Vector3 end = _endTr ? _endTr.position : (endAnchor ? endAnchor.position : transform.position); Vector3 end = GetEndPosition();
int last = _physicsNodes - 1; int last = _physicsNodes - 1;
for (int i = 0; i <= last; i++) for (int i = 0; i <= last; i++)
@@ -397,7 +387,7 @@ public class FishingNodeRope : MonoBehaviour
_pCurr = new Vector3[maxPhysicsNodes]; _pCurr = new Vector3[maxPhysicsNodes];
_pPrev = new Vector3[maxPhysicsNodes]; _pPrev = new Vector3[maxPhysicsNodes];
Vector3 start = startAnchor ? startAnchor.position : transform.position; Vector3 start = GetStartPosition();
Vector3 dir = Vector3.down; Vector3 dir = Vector3.down;
for (int i = 0; i < _physicsNodes; i++) for (int i = 0; i < _physicsNodes; i++)
@@ -505,7 +495,6 @@ public class FishingNodeRope : MonoBehaviour
{ {
if (!startAnchor || !endAnchor) return; if (!startAnchor || !endAnchor) return;
RefreshAnchorTransforms();
RefreshVisibilityState(); RefreshVisibilityState();
if (_isCulledByVisibility) if (_isCulledByVisibility)
return; return;
@@ -525,11 +514,10 @@ public class FishingNodeRope : MonoBehaviour
Simulate_VerletFast(); Simulate_VerletFast();
for (int it = 0; it < iterations; it++) for (int it = 0; it < iterations; it++)
{ {
LockAnchorsHard(); LockAnchorsHard();
SolveDistanceConstraints_HeadOnly_Fast(); SolveDistanceConstraintsBidirectional(stiffness);
} }
SolveHardDistanceConstraints(hardTightenIterations); SolveHardDistanceConstraints(hardTightenIterations);
@@ -566,7 +554,6 @@ public class FishingNodeRope : MonoBehaviour
{ {
if (!startAnchor || !endAnchor || _pCurr == null || _physicsNodes < 2) return; if (!startAnchor || !endAnchor || _pCurr == null || _physicsNodes < 2) return;
RefreshAnchorTransforms();
if (_isCulledByVisibility) if (_isCulledByVisibility)
return; return;
@@ -574,13 +561,11 @@ public class FishingNodeRope : MonoBehaviour
int last = _physicsNodes - 1; int last = _physicsNodes - 1;
Vector3 s = _startTr.position; Vector3 s = GetStartPosition();
Vector3 e = _endTr.position; Vector3 e = GetEndPosition();
_pCurr[0] = s; _pCurr[0] = s;
_pCurr[last] = e; _pCurr[last] = e;
// _pPrev[0] = s;
// _pPrev[last] = e;
DrawHighResLine_Fast(); DrawHighResLine_Fast();
} }
@@ -638,7 +623,7 @@ public class FishingNodeRope : MonoBehaviour
Array.Copy(_pCurr, 1, _pCurr, 1 + addCount, oldCount - 1); Array.Copy(_pCurr, 1, _pCurr, 1 + addCount, oldCount - 1);
Array.Copy(_pPrev, 1, _pPrev, 1 + addCount, oldCount - 1); Array.Copy(_pPrev, 1, _pPrev, 1 + addCount, oldCount - 1);
Vector3 s = _startTr ? _startTr.position : startAnchor.position; Vector3 s = GetStartPosition();
Vector3 dir = Vector3.down; Vector3 dir = Vector3.down;
int firstOld = 1 + addCount; int firstOld = 1 + addCount;
@@ -710,8 +695,8 @@ public class FishingNodeRope : MonoBehaviour
{ {
if (!startAnchor || !endAnchor || _pCurr == null || _pPrev == null || _physicsNodes < 2) return; if (!startAnchor || !endAnchor || _pCurr == null || _pPrev == null || _physicsNodes < 2) return;
Vector3 s = _startTr ? _startTr.position : startAnchor.position; Vector3 s = GetStartPosition();
Vector3 e = _endTr ? _endTr.position : endAnchor.position; Vector3 e = GetEndPosition();
_pCurr[0] = s; _pCurr[0] = s;
_pPrev[0] = s - startAnchor.linearVelocity * _dt; _pPrev[0] = s - startAnchor.linearVelocity * _dt;
@@ -721,26 +706,16 @@ public class FishingNodeRope : MonoBehaviour
_pPrev[last] = e - endAnchor.linearVelocity * _dt; _pPrev[last] = e - endAnchor.linearVelocity * _dt;
} }
private void SolveDistanceConstraints_HeadOnly_Fast()
{
SolveDistanceConstraints_HeadOnly_Bidirectional(stiffness);
}
private void SolveHardDistanceConstraints(int extraIterations) private void SolveHardDistanceConstraints(int extraIterations)
{ {
for (int it = 0; it < extraIterations; it++) for (int it = 0; it < extraIterations; it++)
{ {
LockAnchorsHard(); LockAnchorsHard();
SolveDistanceConstraints_HeadOnly_Hard(); SolveDistanceConstraintsBidirectional(1f);
} }
} }
private void SolveDistanceConstraints_HeadOnly_Hard() private void SolveDistanceConstraintsBidirectional(float combinedStiffness)
{
SolveDistanceConstraints_HeadOnly_Bidirectional(1f);
}
private void SolveDistanceConstraints_HeadOnly_Bidirectional(float combinedStiffness)
{ {
int last = _physicsNodes - 1; int last = _physicsNodes - 1;
if (last <= 0) return; if (last <= 0) return;
@@ -793,47 +768,10 @@ public class FishingNodeRope : MonoBehaviour
{ {
if (groundMask == 0) return; if (groundMask == 0) return;
int last = _physicsNodes - 1; ApplySampledConstraint(SampleConstraintMode.GroundMinY, groundSampleStep, groundInterpolate);
int step = Mathf.Max(1, groundSampleStep);
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)
{
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
{
for (int idx = prevSampleIdx + 1; idx < i; idx++)
ApplyMinY(idx, prevMinY);
}
prevSampleIdx = i;
prevMinY = nextMinY;
}
for (int i = prevSampleIdx + 1; i < last; i++)
ApplyMinY(i, prevMinY);
} }
private float SampleMinY(Vector3 p) private float SampleGroundMinY(Vector3 p)
{ {
Vector3 origin = p + Vector3.up * groundCastHeight; Vector3 origin = p + Vector3.up * groundCastHeight;
if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask, if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask,
@@ -865,46 +803,87 @@ public class FishingNodeRope : MonoBehaviour
int last = _physicsNodes - 1; int last = _physicsNodes - 1;
if (last <= 1) return; if (last <= 1) return;
int step = Mathf.Max(1, waterSampleStep);
float surfaceY = waterLevelY + waterSurfaceOffset; float surfaceY = waterLevelY + waterSurfaceOffset;
bool startUnderWater = _pCurr[0].y < surfaceY; bool startUnderWater = _pCurr[0].y < surfaceY;
int startAdjacentIdx = GetStartAdjacentNodeIndex(last); int startAdjacentIdx = GetStartAdjacentNodeIndex(last);
int prevSampleIdx = 1; ApplySampledConstraint(
float prevSurfaceY = surfaceY; SampleConstraintMode.WaterSurfaceY,
waterSampleStep,
waterInterpolate,
surfaceY,
startUnderWater,
startAdjacentIdx
);
}
ApplyWaterSurface(prevSampleIdx, prevSurfaceY, startUnderWater, startAdjacentIdx); private void ApplySampledConstraint(SampleConstraintMode mode, int sampleStep, bool interpolate,
float constantValue = 0f, bool startUnderWater = false, int startAdjacentIdx = -1)
{
int last = _physicsNodes - 1;
if (last <= 1) return;
int step = Mathf.Max(1, sampleStep);
int prevSampleIdx = 1;
float prevValue = GetConstraintSampleValue(mode, prevSampleIdx, constantValue);
ApplyConstraintValue(mode, prevSampleIdx, prevValue, startUnderWater, startAdjacentIdx);
for (int i = 1 + step; i < last; i += step) for (int i = 1 + step; i < last; i += step)
{ {
float nextSurfaceY = surfaceY; float nextValue = GetConstraintSampleValue(mode, i, constantValue);
ApplyWaterSurface(i, nextSurfaceY, startUnderWater, startAdjacentIdx); ApplyConstraintValue(mode, i, nextValue, startUnderWater, startAdjacentIdx);
if (waterInterpolate) if (interpolate)
{ {
int a = prevSampleIdx; int span = i - prevSampleIdx;
int b = i;
int span = b - a;
for (int j = 1; j < span; j++) for (int j = 1; j < span; j++)
{ {
int idx = a + j; int idx = prevSampleIdx + j;
float t = j / (float)span; float t = j / (float)span;
float y = Mathf.Lerp(prevSurfaceY, nextSurfaceY, t); float value = Mathf.Lerp(prevValue, nextValue, t);
ApplyWaterSurface(idx, y, startUnderWater, startAdjacentIdx); ApplyConstraintValue(mode, idx, value, startUnderWater, startAdjacentIdx);
} }
} }
else else
{ {
for (int idx = prevSampleIdx + 1; idx < i; idx++) for (int idx = prevSampleIdx + 1; idx < i; idx++)
ApplyWaterSurface(idx, prevSurfaceY, startUnderWater, startAdjacentIdx); ApplyConstraintValue(mode, idx, prevValue, startUnderWater, startAdjacentIdx);
} }
prevSampleIdx = i; prevSampleIdx = i;
prevSurfaceY = nextSurfaceY; prevValue = nextValue;
} }
for (int i = prevSampleIdx + 1; i < last; i++) for (int i = prevSampleIdx + 1; i < last; i++)
ApplyWaterSurface(i, prevSurfaceY, startUnderWater, startAdjacentIdx); ApplyConstraintValue(mode, i, prevValue, startUnderWater, startAdjacentIdx);
}
private float GetConstraintSampleValue(SampleConstraintMode mode, int index, float constantValue)
{
switch (mode)
{
case SampleConstraintMode.GroundMinY:
return SampleGroundMinY(_pCurr[index]);
case SampleConstraintMode.WaterSurfaceY:
return constantValue;
default:
return constantValue;
}
}
private void ApplyConstraintValue(SampleConstraintMode mode, int index, float value, bool startUnderWater,
int startAdjacentIdx)
{
switch (mode)
{
case SampleConstraintMode.GroundMinY:
ApplyMinY(index, value);
break;
case SampleConstraintMode.WaterSurfaceY:
ApplyWaterSurface(index, value, startUnderWater, startAdjacentIdx);
break;
}
} }
private int GetStartAdjacentNodeIndex(int last) private int GetStartAdjacentNodeIndex(int last)
@@ -958,13 +937,6 @@ public class FishingNodeRope : MonoBehaviour
int subdiv = PickRenderSubdivisions_Fast(); int subdiv = PickRenderSubdivisions_Fast();
TCaches tc = (subdiv == renderSubdivisionsMoving) ? _tMoving : _tIdle; TCaches tc = (subdiv == renderSubdivisionsMoving) ? _tMoving : _tIdle;
int needed = (_physicsNodes - 1) * subdiv + 1;
if (needed > _rCapacity)
{
_rCapacity = needed;
_rPoints = new Vector3[_rCapacity];
}
int idx = 0; int idx = 0;
int last = _physicsNodes - 1; int last = _physicsNodes - 1;