修改鱼线

This commit is contained in:
2026-03-12 23:08:05 +08:00
parent 98cd3a4aba
commit 2be6ab915a
8 changed files with 222 additions and 241 deletions

View File

@@ -22,7 +22,7 @@ namespace NBF
#region Rod专属
private bool _stretchRope;
private bool _stretchRope = true;
public bool StretchRope
{

View File

@@ -30,6 +30,7 @@ namespace NBF
{
protected override void Update(PlayerInput self)
{
self.UpdateInput();
self.UpdateMove();
}
}
@@ -46,6 +47,26 @@ namespace NBF
#region Input
private void UpdateInput()
{
if (Input.GetKeyDown(KeyCode.Alpha0))
{
// SetLineLength(lineLength);
}
else if (Input.GetKeyDown(KeyCode.Plus) || Input.GetKeyDown(KeyCode.Equals))
{
Player.HandItem.LineLength += 0.1f;
// lineLength += 0.1f;
// SetLineLength(lineLength);
}
else if (Input.GetKeyDown(KeyCode.Minus))
{
Player.HandItem.LineLength -= 0.1f;
// lineLength -= 0.1f;
// SetLineLength(lineLength);
}
}
private void AddInputEvent()
{
InputManager.OnPlayerPerformed += OnPlayerCanceled;

View File

@@ -55,17 +55,40 @@ public class Rope : MonoBehaviour
[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;
[SerializeField, Range(1, 8), Tooltip("每隔多少个节点做一次地面检测;越大越省")]
private int groundSampleStep = 3;
[SerializeField, Tooltip("未采样的点用插值还是直接拷贝邻近采样值")]
private bool groundInterpolate = true;
[SerializeField, Range(1, 8), Tooltip("每隔多少次FixedUpdate更新一次地面约束")]
private int groundUpdateEvery = 2;
private int _groundFrameCounter;
[Header("Simple Water Float (Cheap)")]
[SerializeField, Tooltip("绳子落到水面以下时,是否把节点约束回水面")]
private bool constrainToWaterSurface = true;
[SerializeField, Tooltip("静态水面高度;如果你后面接波浪水面,可改成采样函数")]
private float waterLevelY = 0f;
[SerializeField, Min(0f), Tooltip("把线抬到水面上方一点,避免视觉穿插")]
private float waterSurfaceOffset = 0.002f;
[SerializeField, Range(1, 8), Tooltip("每隔多少个节点做一次水面约束采样;越大越省")]
private int waterSampleStep = 2;
[SerializeField, Tooltip("未采样节点是否插值水面高度")]
private bool waterInterpolate = true;
[SerializeField, Range(1, 8), Tooltip("每隔多少次FixedUpdate更新一次水面约束")]
private int waterUpdateEvery = 1;
[SerializeField, Range(0, 8), Tooltip("水面约束后,再做几次长度约束,减少局部折角")]
private int waterPostConstraintIterations = 2;
private int _waterFrameCounter;
[Header("Render (High Resolution)")] [SerializeField, Min(1), Tooltip("静止时每段物理线段插值加密数量(越大越顺,越耗)")]
private int renderSubdivisionsIdle = 6;
@@ -146,7 +169,6 @@ public class Rope : MonoBehaviour
InitLengthSystem();
AllocateAndInitNodes();
// ✅ 渲染点一次性分配到最大: (maxNodes-1)*idle + 1
int maxSubdiv = Mathf.Max(1, renderSubdivisionsIdle);
_rCapacity = (maxPhysicsNodes - 1) * maxSubdiv + 1;
_rPoints = new Vector3[_rCapacity];
@@ -172,6 +194,14 @@ public class Rope : MonoBehaviour
headMinLen = Mathf.Max(headMinLen, 0.0001f);
nodeHysteresis = Mathf.Max(0f, nodeHysteresis);
groundSampleStep = Mathf.Max(1, groundSampleStep);
groundUpdateEvery = Mathf.Max(1, groundUpdateEvery);
waterSampleStep = Mathf.Max(1, waterSampleStep);
waterUpdateEvery = Mathf.Max(1, waterUpdateEvery);
waterSurfaceOffset = Mathf.Max(0f, waterSurfaceOffset);
waterPostConstraintIterations = Mathf.Clamp(waterPostConstraintIterations, 0, 8);
}
private void InitLengthSystem()
@@ -235,7 +265,6 @@ public class Rope : MonoBehaviour
public void SetTargetLength(float lengthMeters) => _targetLength = Mathf.Max(0f, lengthMeters);
public float GetCurrentLength() => _currentLength;
public float GetTargetLength() => _targetLength;
public float GetLengthSmoothVel() => _lengthSmoothVel;
public float GetLengthByPoints()
@@ -256,20 +285,16 @@ public class Rope : MonoBehaviour
return totalLength;
}
private void FixedUpdate()
{
if (!startAnchor || !endAnchor) return;
// cache dt
_dt = Time.fixedDeltaTime;
if (_dt < 1e-6f) _dt = 1e-6f;
_dt2 = _dt * _dt;
// gravity
_gravity.y = -gravityStrength;
// drag cachesexp 比较贵,但这里每 FixedUpdate 一次OK
_kY = Mathf.Exp(-airDrag * _dt);
_kXZ = Mathf.Exp(-airDragXZ * _dt);
@@ -279,10 +304,8 @@ public class Rope : MonoBehaviour
Simulate_VerletFast();
// anchors
LockAnchorsHard();
// constraints
for (int it = 0; it < iterations; it++)
SolveDistanceConstraints_HeadOnly_Fast();
@@ -298,6 +321,20 @@ public class Rope : MonoBehaviour
}
}
if (constrainToWaterSurface)
{
_waterFrameCounter++;
if (_waterFrameCounter >= waterUpdateEvery)
{
_waterFrameCounter = 0;
ConstrainToWaterSurface();
// 水面抬升后补几次长度约束,让形状更顺一点
for (int it = 0; it < waterPostConstraintIterations; it++)
SolveDistanceConstraints_HeadOnly_Fast();
}
}
LockAnchorsHard();
}
@@ -307,7 +344,6 @@ public class Rope : MonoBehaviour
int last = _physicsNodes - 1;
// 用缓存 transform避免多次属性链
Vector3 s = _startTr.position;
Vector3 e = _endTr.position;
@@ -332,6 +368,20 @@ public class Rope : MonoBehaviour
Mathf.Infinity,
Time.fixedDeltaTime
);
// 长度变化时额外压一点速度,减少收放线时抖动
float delta = Mathf.Abs(_targetLength - _currentLength);
if (delta > 0.0001f && lengthChangeVelocityKill > 0f)
{
float keep = 1f - Mathf.Clamp01(lengthChangeVelocityKill);
for (int i = 1; i < _physicsNodes - 1; i++)
{
Vector3 curr = _pCurr[i];
Vector3 prev = _pPrev[i];
Vector3 disp = curr - prev;
_pPrev[i] = curr - disp * keep;
}
}
}
private void UpdateNodesFromLength()
@@ -369,7 +419,6 @@ public class Rope : MonoBehaviour
if (sq > 1e-6f) dir = toOld1 / Mathf.Sqrt(sq);
}
// inherit displacement (Verlet)
Vector3 inheritDisp = Vector3.zero;
if (oldCount >= 2 && firstOld < maxPhysicsNodes)
inheritDisp = (_pCurr[firstOld] - _pPrev[firstOld]);
@@ -378,7 +427,7 @@ public class Rope : MonoBehaviour
{
Vector3 pos = s + dir * (physicsSegmentLen * k);
_pCurr[k] = pos;
_pPrev[k] = pos - inheritDisp; // 保持动感
_pPrev[k] = pos - inheritDisp;
}
LockAnchorsHard();
@@ -408,13 +457,8 @@ public class Rope : MonoBehaviour
_headRestLen = Mathf.Clamp(_headRestLen, headMinLen, physicsSegmentLen * 1.5f);
}
/// <summary>
/// ✅ 更快的 Verlet去掉 /dt 和 *dt 抵消的无效计算
/// </summary>
private void Simulate_VerletFast()
{
// displacement = curr - prev
// next = curr + displacement*drag*dampen + gravity*dt^2
for (int i = 1; i < _physicsNodes - 1; i++)
{
Vector3 disp = _pCurr[i] - _pPrev[i];
@@ -447,9 +491,6 @@ public class Rope : MonoBehaviour
_pPrev[last] = e - endAnchor.linearVelocity * _dt;
}
/// <summary>
/// ✅ 约束:减少临时变量、用 sqrMagnitude + invDist
/// </summary>
private void SolveDistanceConstraints_HeadOnly_Fast()
{
int last = _physicsNodes - 1;
@@ -466,36 +507,14 @@ public class Rope : MonoBehaviour
if (sq < 1e-12f) continue;
float dist = Mathf.Sqrt(sq);
float diff = (dist - rest) / dist; // = 1 - rest/dist
float diff = (dist - rest) / dist;
Vector3 corr = delta * (diff * stiffness);
// i==0 锚点固定last 锚点固定
if (i != 0) _pCurr[i] = a + corr * 0.5f;
if (i + 1 != last) _pCurr[i + 1] = b - corr * 0.5f;
}
}
// private void ConstrainToGround()
// {
// if (groundMask == 0) return;
//
// // RaycastHit 是 struct这里不会 GC
// for (int i = 1; i < _physicsNodes - 1; i++)
// {
// Vector3 p = _pCurr[i];
// Vector3 origin = p + Vector3.up * groundCastHeight;
//
// if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask,
// QueryTriggerInteraction.Ignore))
// {
// float minY = hit.point.y + groundRadius;
// if (p.y < minY) p.y = minY;
// }
//
// _pCurr[i] = p;
// }
// }
private void ConstrainToGround()
{
if (groundMask == 0) return;
@@ -503,12 +522,9 @@ public class Rope : MonoBehaviour
int last = _physicsNodes - 1;
int step = Mathf.Max(1, groundSampleStep);
// 记录采样点的“最低允许Y”
// 不想分配数组就用局部变量滚动插值
int prevSampleIdx = 1;
float prevMinY = SampleMinY(_pCurr[prevSampleIdx]);
// 把采样点先处理掉
ApplyMinY(prevSampleIdx, prevMinY);
for (int i = 1 + step; i < last; i += step)
@@ -518,7 +534,6 @@ public class Rope : MonoBehaviour
if (groundInterpolate)
{
// 在两个采样点之间插值 minY视觉更平滑
int a = prevSampleIdx;
int b = i;
int span = b - a;
@@ -532,7 +547,6 @@ public class Rope : MonoBehaviour
}
else
{
// 直接用 prevMinY 填充中间点(更省)
for (int idx = prevSampleIdx + 1; idx < i; idx++)
ApplyMinY(idx, prevMinY);
}
@@ -541,7 +555,6 @@ public class Rope : MonoBehaviour
prevMinY = nextMinY;
}
// 尾巴剩余部分用最后一个采样值填
for (int i = prevSampleIdx + 1; i < last; i++)
ApplyMinY(i, prevMinY);
}
@@ -553,16 +566,84 @@ public class Rope : MonoBehaviour
QueryTriggerInteraction.Ignore))
return hit.point.y + groundRadius;
// 没命中就不抬(返回极小值)
return float.NegativeInfinity;
}
private void ApplyMinY(int i, float minY)
{
if (float.IsNegativeInfinity(minY)) return;
Vector3 p = _pCurr[i];
if (p.y < minY) p.y = minY;
_pCurr[i] = p;
if (p.y < minY)
{
p.y = minY;
_pCurr[i] = p;
// prev 同步抬上来,避免下一帧又被惯性拉回去造成抖动
Vector3 prev = _pPrev[i];
if (prev.y < minY) prev.y = minY;
_pPrev[i] = prev;
}
}
private void ConstrainToWaterSurface()
{
int last = _physicsNodes - 1;
if (last <= 1) return;
int step = Mathf.Max(1, waterSampleStep);
float surfaceY = waterLevelY + waterSurfaceOffset;
int prevSampleIdx = 1;
float prevSurfaceY = surfaceY;
ApplyWaterSurface(prevSampleIdx, prevSurfaceY);
for (int i = 1 + step; i < last; i += step)
{
float nextSurfaceY = surfaceY;
ApplyWaterSurface(i, nextSurfaceY);
if (waterInterpolate)
{
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 y = Mathf.Lerp(prevSurfaceY, nextSurfaceY, t);
ApplyWaterSurface(idx, y);
}
}
else
{
for (int idx = prevSampleIdx + 1; idx < i; idx++)
ApplyWaterSurface(idx, prevSurfaceY);
}
prevSampleIdx = i;
prevSurfaceY = nextSurfaceY;
}
for (int i = prevSampleIdx + 1; i < last; i++)
ApplyWaterSurface(i, prevSurfaceY);
}
private void ApplyWaterSurface(int i, float surfaceY)
{
Vector3 p = _pCurr[i];
if (p.y < surfaceY)
{
p.y = surfaceY;
_pCurr[i] = p;
// 同步 prev杀掉向下惯性避免反复穿透水面
Vector3 prev = _pPrev[i];
if (prev.y < surfaceY) prev.y = surfaceY;
_pPrev[i] = prev;
}
}
private void DrawHighResLine_Fast()
@@ -586,8 +667,6 @@ public class Rope : MonoBehaviour
int needed = (_physicsNodes - 1) * subdiv + 1;
if (needed > _rCapacity)
{
// 理论上不该发生_rCapacity 用 maxNodes & idle 分配)
// 保险扩容一次
_rCapacity = needed;
_rPoints = new Vector3[_rCapacity];
}
@@ -615,7 +694,6 @@ public class Rope : MonoBehaviour
float t2 = tc.t2[s];
float t3 = tc.t3[s];
// inline CatmullRom少一次函数调用
Vector3 cr =
0.5f * (
(2f * p1) +
@@ -624,7 +702,6 @@ public class Rope : MonoBehaviour
(-p0 + 3f * p1 - 3f * p2 + p3) * t3
);
// Linear Y
cr.y = p1.y + (p2.y - p1.y) * t;
_rPoints[idx++] = cr;
@@ -637,16 +714,13 @@ public class Rope : MonoBehaviour
_lineRenderer.SetPositions(_rPoints);
}
/// <summary>
/// ✅ 用 sqrMagnitude 比较阈值,避免 sqrt
/// </summary>
private int PickRenderSubdivisions_Fast()
{
int idle = Mathf.Max(1, renderSubdivisionsIdle);
int moving = Mathf.Max(1, renderSubdivisionsMoving);
float thr = movingSpeedThreshold;
float thrSq = (thr * _dt) * (thr * _dt); // 因为我们用 disp = curr-prev单位是米/step所以阈值要乘 dt
float thrSq = (thr * _dt) * (thr * _dt);
float sumSq = 0f;
int count = Mathf.Max(1, _physicsNodes - 2);

View File

@@ -32,9 +32,9 @@ namespace NBF
{
await LoginHelper.Login(InputAccount.text);
// await Fishing.Instance.Go(RoleModel.Instance.Info.MapId);
await Fishing.Instance.Go(RoleModel.Instance.Info.MapId);
ChatTestPanel.Show();
// ChatTestPanel.Show();
// FishingShopPanel.Show();