修改
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user