提交修改

This commit is contained in:
2026-05-08 23:45:29 +08:00
parent 3b587e6c4f
commit e42f540822
4 changed files with 90 additions and 52 deletions

View File

@@ -47,7 +47,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
startAnchor: {fileID: 5597807613657979793}
endAnchor: {fileID: 54298866000586118}
LineMultiple: 5
LineMultiple: 1
physicsSegmentLen: 0.1
minPhysicsNodes: 2
maxPhysicsNodes: 200
@@ -56,6 +56,8 @@ MonoBehaviour:
stiffness: 0.8
iterations: 10
hardTightenIterations: 2
adaptiveHardTightenMaxIterations: 8
hardConstraintTolerance: 0.0005
initialLength: 0
lengthSmoothTime: 0.15
lengthChangeVelocityKill: 0.4
@@ -217,7 +219,7 @@ GameObject:
- component: {fileID: 2951454344396477079}
- component: {fileID: 2305106969988397276}
- component: {fileID: 6377942246174119720}
m_Layer: 7
m_Layer: 15
m_Name: End
m_TagString: Untagged
m_Icon: {fileID: 0}
@@ -493,7 +495,7 @@ GameObject:
- component: {fileID: 4367274852511404246}
- component: {fileID: 8491405271793597799}
- component: {fileID: 6946262978138518655}
m_Layer: 16
m_Layer: 15
m_Name: Float
m_TagString: Untagged
m_Icon: {fileID: 0}
@@ -766,7 +768,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
startAnchor: {fileID: 54298866000586118}
endAnchor: {fileID: 54679398375713381}
LineMultiple: 5
LineMultiple: 1
physicsSegmentLen: 0.2
minPhysicsNodes: 2
maxPhysicsNodes: 120
@@ -775,6 +777,8 @@ MonoBehaviour:
stiffness: 0.8
iterations: 10
hardTightenIterations: 2
adaptiveHardTightenMaxIterations: 8
hardConstraintTolerance: 0.0005
initialLength: 0
lengthSmoothTime: 0.15
lengthChangeVelocityKill: 0.6
@@ -933,7 +937,7 @@ GameObject:
- component: {fileID: 9208415877353988341}
- component: {fileID: 5597807613657979793}
- component: {fileID: 3463242999848273700}
m_Layer: 0
m_Layer: 15
m_Name: Start
m_TagString: Untagged
m_Icon: {fileID: 0}

View File

@@ -99,7 +99,7 @@ namespace NBF
BobberNode.gameObject.SetActive(false);
BobberNode.Rope.gameObject.SetActive(false);
EndNode.SetConnectedBody(StartNode.Rigidbody);
EndNode.SetLenght(totalLenght);
EndNode.SetLenght(totalLenght + 0.2f);
}
else
{

View File

@@ -50,7 +50,7 @@ namespace NBF
if (rope)
{
rope.SetTargetLength(lenght);
rope.SetTargetLength(lenght - lenght * 0.02f);
}
}

View File

@@ -14,23 +14,29 @@ public class Rope : MonoBehaviour
[Header("Physics (Dynamic Nodes, Fixed Segment Len)")] [SerializeField, Min(0.01f), Tooltip("物理每段固定长度(越小越细致越耗)")]
private float physicsSegmentLen = 0.15f;
[SerializeField, Range(2, 200)] private int minPhysicsNodes = 12;
[SerializeField, Range(2, 200)] private int minPhysicsNodes = 2;
[SerializeField, Range(2, 400), Tooltip("物理节点上限(仅用于性能保护;与“最大长度不限制”不是一回事)")]
private int maxPhysicsNodes = 120;
private int maxPhysicsNodes = 200;
[SerializeField] private float gravityStrength = 2.0f;
[SerializeField] private float gravityStrength = 6.0f;
[SerializeField, Range(0f, 1f)] private float velocityDampen = 0.95f;
[SerializeField, Range(0.0f, 1.0f), Tooltip("约束修正强度越大越硬。0.6~0.9 常用")]
private float stiffness = 0.8f;
[SerializeField, Range(1, 80), Tooltip("迭代次数。鱼线 10~30 通常够用")]
private int iterations = 20;
[SerializeField, Range(1, 80), Tooltip("迭代次数")]
private int iterations = 10;
[SerializeField, Range(0, 16), Tooltip("主求解后追加的硬长度约束次数。只负责把 poly 拉回到 rest total不改变可变长度逻辑")]
private int hardTightenIterations = 2;
[SerializeField, Range(0, 32), Tooltip("当绳子接近拉直时,按误差自动追加的硬长度约束次数上限")]
private int adaptiveHardTightenMaxIterations = 8;
[SerializeField, Min(0f), Tooltip("单段允许的最大超长误差;超过时继续追加硬长度约束")]
private float hardConstraintTolerance = 0.0005f;
[Header("Length Control (No Min/Max Clamp)")]
[Tooltip("初始总长度(米)。如果为 0则用 physicsSegmentLen*(minPhysicsNodes-1) 作为初始长度")]
[SerializeField, Min(0f)]
@@ -40,7 +46,7 @@ public class Rope : MonoBehaviour
private float lengthSmoothTime = 0.15f;
[Tooltip("当长度在变化时额外把速度压掉一些防抖。0=不额外处理1=变化时几乎清速度(建议只在收线生效)")] [SerializeField, Range(0f, 1f)]
private float lengthChangeVelocityKill = 0.6f;
private float lengthChangeVelocityKill = 0.4f;
[Tooltip("允许的最小松弛余量(避免目标长度刚好等于锚点距离时抖动)")] [SerializeField, Min(0f)]
private float minSlack = 0.002f;
@@ -52,9 +58,9 @@ public class Rope : MonoBehaviour
private float nodeHysteresis = 0.05f;
[Header("Simple Ground/Water Constraint (Cheap)")] [SerializeField]
private bool constrainToGround = true;
private bool constrainToGround = false;
[SerializeField] private LayerMask groundMask = ~0;
[SerializeField] private LayerMask groundMask = 0;
[SerializeField, Min(0f)] private float groundRadius = 0.01f;
[SerializeField, Min(0f)] private float groundCastHeight = 1.0f;
[SerializeField, Min(0.01f)] private float groundCastDistance = 2.5f;
@@ -66,7 +72,7 @@ public class Rope : MonoBehaviour
private bool groundInterpolate = true;
[SerializeField, Range(1, 8), Tooltip("每隔多少次FixedUpdate更新一次地面约束")]
private int groundUpdateEvery = 2;
private int groundUpdateEvery = 1;
[SerializeField, Range(0, 8), Tooltip("地面约束后,再做几次长度约束,减少 poly 被地面抬长")]
private int groundPostConstraintIterations = 2;
@@ -74,7 +80,7 @@ public class Rope : MonoBehaviour
private int _groundFrameCounter;
[Header("Simple Water Float (Cheap)")] [SerializeField, Tooltip("绳子落到水面以下时,是否把节点约束回水面")]
private bool constrainToWaterSurface = true;
private bool constrainToWaterSurface = false;
[SerializeField, Tooltip("静态水面高度;如果你后面接波浪水面,可改成采样函数")]
private float waterLevelY = 0f;
@@ -129,7 +135,7 @@ public class Rope : MonoBehaviour
private float visibilityViewportPadding = 0.08f;
[Header("Air Drag (Stable)")] [SerializeField, Range(0f, 5f), Tooltip("空气阻力Y向指数衰减越大越不飘")]
private float airDrag = 0.9f;
private float airDrag = 0.2f;
[SerializeField, Range(0f, 2f), Tooltip("横向额外阻力XZ指数衰减越大越不左右飘")]
private float airDragXZ = 0.6f;
@@ -172,7 +178,7 @@ public class Rope : MonoBehaviour
private bool _isCulledByVisibility;
private int _tIdleSubdiv = -1;
private int _tMovingSubdiv = -1;
// Catmull t caches只缓存 idle/moving 两档,减少每帧重复乘法)
private struct TCaches
@@ -198,12 +204,15 @@ public class Rope : MonoBehaviour
RefreshVisibilityState(true);
}
private void OnValidate()
{
renderSubdivisionsIdle = Mathf.Max(renderSubdivisionsIdle, 1);
renderSubdivisionsMoving = Mathf.Max(renderSubdivisionsMoving, 1);
iterations = Mathf.Clamp(iterations, 1, 80);
hardTightenIterations = Mathf.Clamp(hardTightenIterations, 0, 16);
adaptiveHardTightenMaxIterations = Mathf.Clamp(adaptiveHardTightenMaxIterations, 0, 32);
hardConstraintTolerance = Mathf.Max(0f, hardConstraintTolerance);
groundCastDistance = Mathf.Max(groundCastDistance, 0.01f);
groundCastHeight = Mathf.Max(groundCastHeight, 0f);
lineWidth = Mathf.Max(lineWidth, 0.0001f);
@@ -236,15 +245,6 @@ public class Rope : MonoBehaviour
_endTr = endAnchor ? endAnchor.transform : null;
}
private bool ShouldAlwaysSimulate()
{
if (!localOwnerAlwaysSimulate)
return false;
// var owner = _rod?.PlayerItem?.Owner;
// return owner == null || owner.IsSelf;
return true;
}
private Transform GetActiveCameraTransform()
{
@@ -298,7 +298,7 @@ public class Rope : MonoBehaviour
private void RefreshVisibilityState(bool force = false)
{
if (!cullRemoteRopeWhenInvisible || ShouldAlwaysSimulate())
if (!cullRemoteRopeWhenInvisible)
{
_isCulledByVisibility = false;
if (_lineRenderer)
@@ -526,6 +526,7 @@ public class Rope : MonoBehaviour
}
SolveHardDistanceConstraints(hardTightenIterations);
SolveHardDistanceConstraintsAdaptive();
LockAnchorsHard();
if (constrainToGround)
@@ -566,16 +567,7 @@ public class Rope : MonoBehaviour
EnsureRenderCaches();
int last = _physicsNodes - 1;
Vector3 s = _startTr.position;
Vector3 e = _endTr.position;
_pCurr[0] = s;
_pCurr[last] = e;
// _pPrev[0] = s;
// _pPrev[last] = e;
DrawHighResLine_Fast();
DrawHighResLine_Fast(_startTr.position, _endTr.position, last);
}
private void UpdateLengthSmooth()
@@ -592,9 +584,10 @@ public class Rope : MonoBehaviour
Time.fixedDeltaTime
);
// 长度变化时额外压一点速度,减少收放线时抖动
float delta = Mathf.Abs(_targetLength - _currentLength);
if (delta > 0.0001f && lengthChangeVelocityKill > 0f)
// 仅在收线(目标长度小于当前长度)时额外压速度;
// 放线时不要压速度,否则新增节点下落会出现“顿一下再突然加速”。
float reelInDelta = _currentLength - _targetLength;
if (reelInDelta > 0.0001f && lengthChangeVelocityKill > 0f)
{
float keep = 1f - Mathf.Clamp01(lengthChangeVelocityKill);
for (int i = 1; i < _physicsNodes - 1; i++)
@@ -728,6 +721,21 @@ public class Rope : MonoBehaviour
}
}
private void SolveHardDistanceConstraintsAdaptive()
{
if (adaptiveHardTightenMaxIterations <= 0 || hardConstraintTolerance <= 0f)
return;
for (int it = 0; it < adaptiveHardTightenMaxIterations; it++)
{
if (GetMaxPositiveSegmentDelta() <= hardConstraintTolerance)
break;
LockAnchorsHard();
SolveDistanceConstraints_HeadOnly_Hard();
}
}
private void SolveDistanceConstraints_HeadOnly_Hard()
{
SolveDistanceConstraints_HeadOnly_Bidirectional(1f);
@@ -744,7 +752,8 @@ public class Rope : MonoBehaviour
SolveDistanceConstraintsSweep_Fast(last - 1, -1, -1, last, sweepStiffness);
}
private void SolveDistanceConstraintsSweep_Fast(int start, int endExclusive, int step, int last, float sweepStiffness)
private void SolveDistanceConstraintsSweep_Fast(int start, int endExclusive, int step, int last,
float sweepStiffness)
{
for (int i = start; i != endExclusive; i += step)
{
@@ -781,6 +790,21 @@ public class Rope : MonoBehaviour
}
}
private float GetMaxPositiveSegmentDelta()
{
float maxDelta = 0f;
for (int i = 1; i < _physicsNodes; i++)
{
float rest = (i == 1) ? _headRestLen : physicsSegmentLen;
float segLen = Vector3.Distance(_pCurr[i - 1], _pCurr[i]);
float delta = segLen - rest;
if (delta > maxDelta)
maxDelta = delta;
}
return maxDelta;
}
private void ConstrainToGround()
{
if (groundMask == 0) return;
@@ -932,7 +956,7 @@ public class Rope : MonoBehaviour
}
}
private void DrawHighResLine_Fast()
private void DrawHighResLine_Fast(Vector3 renderStart, Vector3 renderEnd, int last)
{
if (_pCurr == null || _physicsNodes < 2) return;
@@ -943,7 +967,9 @@ public class Rope : MonoBehaviour
if (!smooth)
{
_lineRenderer.positionCount = _physicsNodes;
_lineRenderer.SetPositions(_pCurr);
for (int i = 0; i <= last; i++)
_rPoints[i] = GetRenderPoint(i, last, renderStart, renderEnd);
_lineRenderer.SetPositions(_rPoints);
return;
}
@@ -958,7 +984,6 @@ public class Rope : MonoBehaviour
}
int idx = 0;
int last = _physicsNodes - 1;
for (int seg = 0; seg < last; seg++)
{
@@ -969,10 +994,10 @@ public class Rope : MonoBehaviour
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];
Vector3 p0 = GetRenderPoint(i0, last, renderStart, renderEnd);
Vector3 p1 = GetRenderPoint(i1, last, renderStart, renderEnd);
Vector3 p2 = GetRenderPoint(i2, last, renderStart, renderEnd);
Vector3 p3 = GetRenderPoint(i3, last, renderStart, renderEnd);
for (int s = 0; s < subdiv; s++)
{
@@ -995,12 +1020,21 @@ public class Rope : MonoBehaviour
}
}
_rPoints[idx++] = _pCurr[last];
_rPoints[idx++] = renderEnd;
_lineRenderer.positionCount = idx;
_lineRenderer.SetPositions(_rPoints);
}
private Vector3 GetRenderPoint(int index, int last, Vector3 renderStart, Vector3 renderEnd)
{
if (index <= 0)
return renderStart;
if (index >= last)
return renderEnd;
return _pCurr[index];
}
private static float ClampMonotonic(float value, float p0, float p1, float p2, float p3)
{
bool rising = p0 <= p1 && p1 <= p2 && p2 <= p3;
@@ -1060,4 +1094,4 @@ public class Rope : MonoBehaviour
for (int i = 0; i < _physicsNodes; i++)
Gizmos.DrawSphere(_pCurr[i], 0.01f);
}
}
}