diff --git a/Assets/Scripts/Fishing/New/View/FishingLine/Renderer/FishingNodeRope.cs b/Assets/Scripts/Fishing/New/View/FishingLine/Renderer/FishingNodeRope.cs index 66d39d21c..4577e6689 100644 --- a/Assets/Scripts/Fishing/New/View/FishingLine/Renderer/FishingNodeRope.cs +++ b/Assets/Scripts/Fishing/New/View/FishingLine/Renderer/FishingNodeRope.cs @@ -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(); _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(); - 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); } -} \ No newline at end of file +}