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)]
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)]
private float headMinLen = 0.01f;
@@ -158,16 +155,12 @@ public class FishingNodeRope : 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 Transform _cameraTr;
private Camera _cachedCamera;
private int _visibilityCheckCounter;
private bool _isCulledByVisibility;
private int _tIdleSubdiv = -1;
@@ -192,18 +185,22 @@ public class FishingNodeRope : MonoBehaviour
private TCaches _tIdle;
private TCaches _tMoving;
private enum SampleConstraintMode
{
GroundMinY,
WaterSurfaceY
}
private void Awake()
{
_lineRenderer = GetComponent<LineRenderer>();
_gravity = new Vector3(0f, -gravityStrength, 0f);
RefreshAnchorTransforms();
_dt = Mathf.Max(Time.fixedDeltaTime, 1e-6f);
_dt2 = _dt * _dt;
InitLengthSystem();
AllocateAndInitNodes();
EnsureRenderCaches();
RefreshVisibilityState(true);
}
private void OnValidate()
@@ -238,12 +235,6 @@ public class FishingNodeRope : MonoBehaviour
visibilityViewportPadding = Mathf.Clamp(visibilityViewportPadding, 0f, 0.5f);
}
private void RefreshAnchorTransforms()
{
_startTr = startAnchor ? startAnchor.transform : null;
_endTr = endAnchor ? endAnchor.transform : null;
}
private bool ShouldAlwaysSimulate()
{
if (!localOwnerAlwaysSimulate)
@@ -253,23 +244,28 @@ public class FishingNodeRope : MonoBehaviour
return owner == null || owner.IsSelf;
}
private Transform GetActiveCameraTransform()
private Vector3 GetStartPosition()
{
Camera main = BaseCamera.Main;
if (main)
return startAnchor ? startAnchor.position : transform.position;
}
private Vector3 GetEndPosition()
{
return endAnchor ? endAnchor.position : transform.position;
}
private Camera GetActiveCamera()
{
if (BaseCamera.Main)
{
_cameraTr = main.transform;
return _cameraTr;
_cachedCamera = BaseCamera.Main;
return _cachedCamera;
}
if (!_cameraTr)
{
Camera fallback = Camera.main;
if (fallback)
_cameraTr = fallback.transform;
}
if (!_cachedCamera)
_cachedCamera = Camera.main;
return _cameraTr;
return _cachedCamera;
}
private static bool IsViewportPointVisible(Vector3 viewportPoint, float padding)
@@ -283,18 +279,12 @@ public class FishingNodeRope : MonoBehaviour
private bool IsVisibleToMainCamera()
{
Transform camTr = GetActiveCameraTransform();
if (!camTr)
return true;
Camera cam = camTr.GetComponent<Camera>();
if (!cam)
cam = BaseCamera.Main ? BaseCamera.Main : Camera.main;
Camera cam = GetActiveCamera();
if (!cam)
return true;
Vector3 start = _startTr ? _startTr.position : (startAnchor ? startAnchor.position : transform.position);
Vector3 end = _endTr ? _endTr.position : (endAnchor ? endAnchor.position : transform.position);
Vector3 start = GetStartPosition();
Vector3 end = GetEndPosition();
Vector3 middle = (start + end) * 0.5f;
float padding = visibilityViewportPadding;
@@ -345,8 +335,8 @@ public class FishingNodeRope : MonoBehaviour
if (_physicsNodes < 2)
return;
Vector3 start = _startTr ? _startTr.position : (startAnchor ? startAnchor.position : transform.position);
Vector3 end = _endTr ? _endTr.position : (endAnchor ? endAnchor.position : transform.position);
Vector3 start = GetStartPosition();
Vector3 end = GetEndPosition();
int last = _physicsNodes - 1;
for (int i = 0; i <= last; i++)
@@ -397,7 +387,7 @@ public class FishingNodeRope : MonoBehaviour
_pCurr = new Vector3[maxPhysicsNodes];
_pPrev = new Vector3[maxPhysicsNodes];
Vector3 start = startAnchor ? startAnchor.position : transform.position;
Vector3 start = GetStartPosition();
Vector3 dir = Vector3.down;
for (int i = 0; i < _physicsNodes; i++)
@@ -505,7 +495,6 @@ public class FishingNodeRope : MonoBehaviour
{
if (!startAnchor || !endAnchor) return;
RefreshAnchorTransforms();
RefreshVisibilityState();
if (_isCulledByVisibility)
return;
@@ -525,11 +514,10 @@ public class FishingNodeRope : MonoBehaviour
Simulate_VerletFast();
for (int it = 0; it < iterations; it++)
{
LockAnchorsHard();
SolveDistanceConstraints_HeadOnly_Fast();
SolveDistanceConstraintsBidirectional(stiffness);
}
SolveHardDistanceConstraints(hardTightenIterations);
@@ -566,7 +554,6 @@ public class FishingNodeRope : MonoBehaviour
{
if (!startAnchor || !endAnchor || _pCurr == null || _physicsNodes < 2) return;
RefreshAnchorTransforms();
if (_isCulledByVisibility)
return;
@@ -574,13 +561,11 @@ public class FishingNodeRope : MonoBehaviour
int last = _physicsNodes - 1;
Vector3 s = _startTr.position;
Vector3 e = _endTr.position;
Vector3 s = GetStartPosition();
Vector3 e = GetEndPosition();
_pCurr[0] = s;
_pCurr[last] = e;
// _pPrev[0] = s;
// _pPrev[last] = e;
DrawHighResLine_Fast();
}
@@ -638,7 +623,7 @@ public class FishingNodeRope : MonoBehaviour
Array.Copy(_pCurr, 1, _pCurr, 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;
int firstOld = 1 + addCount;
@@ -710,8 +695,8 @@ public class FishingNodeRope : MonoBehaviour
{
if (!startAnchor || !endAnchor || _pCurr == null || _pPrev == null || _physicsNodes < 2) return;
Vector3 s = _startTr ? _startTr.position : startAnchor.position;
Vector3 e = _endTr ? _endTr.position : endAnchor.position;
Vector3 s = GetStartPosition();
Vector3 e = GetEndPosition();
_pCurr[0] = s;
_pPrev[0] = s - startAnchor.linearVelocity * _dt;
@@ -721,26 +706,16 @@ public class FishingNodeRope : MonoBehaviour
_pPrev[last] = e - endAnchor.linearVelocity * _dt;
}
private void SolveDistanceConstraints_HeadOnly_Fast()
{
SolveDistanceConstraints_HeadOnly_Bidirectional(stiffness);
}
private void SolveHardDistanceConstraints(int extraIterations)
{
for (int it = 0; it < extraIterations; it++)
{
LockAnchorsHard();
SolveDistanceConstraints_HeadOnly_Hard();
SolveDistanceConstraintsBidirectional(1f);
}
}
private void SolveDistanceConstraints_HeadOnly_Hard()
{
SolveDistanceConstraints_HeadOnly_Bidirectional(1f);
}
private void SolveDistanceConstraints_HeadOnly_Bidirectional(float combinedStiffness)
private void SolveDistanceConstraintsBidirectional(float combinedStiffness)
{
int last = _physicsNodes - 1;
if (last <= 0) return;
@@ -793,47 +768,10 @@ public class FishingNodeRope : MonoBehaviour
{
if (groundMask == 0) return;
int last = _physicsNodes - 1;
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);
ApplySampledConstraint(SampleConstraintMode.GroundMinY, groundSampleStep, groundInterpolate);
}
private float SampleMinY(Vector3 p)
private float SampleGroundMinY(Vector3 p)
{
Vector3 origin = p + Vector3.up * groundCastHeight;
if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask,
@@ -865,46 +803,87 @@ public class FishingNodeRope : MonoBehaviour
int last = _physicsNodes - 1;
if (last <= 1) return;
int step = Mathf.Max(1, waterSampleStep);
float surfaceY = waterLevelY + waterSurfaceOffset;
bool startUnderWater = _pCurr[0].y < surfaceY;
int startAdjacentIdx = GetStartAdjacentNodeIndex(last);
int prevSampleIdx = 1;
float prevSurfaceY = surfaceY;
ApplySampledConstraint(
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)
{
float nextSurfaceY = surfaceY;
ApplyWaterSurface(i, nextSurfaceY, startUnderWater, startAdjacentIdx);
float nextValue = GetConstraintSampleValue(mode, i, constantValue);
ApplyConstraintValue(mode, i, nextValue, startUnderWater, startAdjacentIdx);
if (waterInterpolate)
if (interpolate)
{
int a = prevSampleIdx;
int b = i;
int span = b - a;
int span = i - prevSampleIdx;
for (int j = 1; j < span; j++)
{
int idx = a + j;
int idx = prevSampleIdx + j;
float t = j / (float)span;
float y = Mathf.Lerp(prevSurfaceY, nextSurfaceY, t);
ApplyWaterSurface(idx, y, startUnderWater, startAdjacentIdx);
float value = Mathf.Lerp(prevValue, nextValue, t);
ApplyConstraintValue(mode, idx, value, startUnderWater, startAdjacentIdx);
}
}
else
{
for (int idx = prevSampleIdx + 1; idx < i; idx++)
ApplyWaterSurface(idx, prevSurfaceY, startUnderWater, startAdjacentIdx);
ApplyConstraintValue(mode, idx, prevValue, startUnderWater, startAdjacentIdx);
}
prevSampleIdx = i;
prevSurfaceY = nextSurfaceY;
prevValue = nextValue;
}
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)
@@ -958,13 +937,6 @@ public class FishingNodeRope : MonoBehaviour
int subdiv = PickRenderSubdivisions_Fast();
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 last = _physicsNodes - 1;
@@ -1068,4 +1040,4 @@ public class FishingNodeRope : MonoBehaviour
for (int i = 0; i < _physicsNodes; i++)
Gizmos.DrawSphere(_pCurr[i], 0.01f);
}
}
}