提交修改

This commit is contained in:
2026-04-13 23:47:18 +08:00
parent caa260b53b
commit 9c351b3823
20 changed files with 1794 additions and 3830 deletions

View File

@@ -13,9 +13,7 @@ namespace NBF
public override bool IsSupportedNode(FishingLineNode node)
{
return node != null
&& !node.IsRuntimeVirtualNode
&& node.Type != FishingLineNode.NodeType.Start;
return node != null && node.Type != FishingLineNode.NodeType.Start;
}
public override bool CanControl()

View File

@@ -11,31 +11,22 @@ namespace NBF
Start,
Float,
Weight,
Tail,
Virtual,
Tail
}
private FishingLineSolver _solver;
[Header("Node")] [SerializeField] private NodeType nodeType = NodeType.Tail;
[SerializeField] public Rigidbody body;
[SerializeField] private MonoBehaviour interaction;
private ConfigurableJoint _joint;
[Header("Segment To Next Logical Node")] [Min(0f)] [SerializeField]
private float segmentLengthToNext = 0.5f;
[Min(0)] [SerializeField] private int fixedVirtualNodesToNext = 2;
[Header("Runtime")] [SerializeField] private bool runtimeVirtualNode;
[SerializeField] private int runtimeChainIndex = -1;
[Header("Debug")] [SerializeField] private bool drawDebugGizmo = true;
[Min(0.001f)] [SerializeField] private float debugGizmoRadius = 0.03f;
[SerializeField] private Color logicalNodeColor = new(0.2f, 0.9f, 0.2f, 1f);
[SerializeField] private Color virtualNodeColor = new(1f, 0.6f, 0.1f, 1f);
[SerializeField] private List<FishingLineNodeFeature> features = new();
[SerializeField] private List<FishingLineNodeMotionFeature> motionFeatures = new();
private bool featureCacheReady;
@@ -55,40 +46,12 @@ namespace NBF
public Rigidbody Body => body;
public MonoBehaviour Interaction => interaction;
public float SegmentLengthToNext
{
get => segmentLengthToNext;
set => segmentLengthToNext = Mathf.Max(0f, value);
}
public int FixedVirtualNodesToNext
{
get => fixedVirtualNodesToNext;
set => fixedVirtualNodesToNext = Mathf.Max(0, value);
}
public bool IsRuntimeVirtualNode => runtimeVirtualNode;
public ConfigurableJoint Joint => _joint;
public int RuntimeChainIndex => runtimeChainIndex;
public Vector3 Position => transform.position;
public string GetDebugName()
{
return runtimeVirtualNode
? $"V[{runtimeChainIndex}]"
: $"{nodeType}[{runtimeChainIndex}]";
}
public string GetDebugSummary()
{
var bodySummary = body == null
? "NoBody"
: $"v:{body.linearVelocity.magnitude:F2}";
return $"{GetDebugName()} pos:{Position} next:{segmentLengthToNext:F2} {bodySummary}";
}
private void Reset()
{
@@ -98,6 +61,7 @@ namespace NBF
private void Awake()
{
_solver = GetComponentInParent<FishingLineSolver>();
_joint = GetComponent<ConfigurableJoint>();
EnsureFeatureCache();
}
@@ -120,24 +84,8 @@ namespace NBF
}
segmentLengthToNext = Mathf.Max(0f, segmentLengthToNext);
fixedVirtualNodesToNext = Mathf.Max(0, fixedVirtualNodesToNext);
}
public void SetRuntimeVirtual(bool isVirtual, int chainIndex)
{
runtimeVirtualNode = isVirtual;
runtimeChainIndex = chainIndex;
if (isVirtual)
{
nodeType = NodeType.Virtual;
}
}
public void SetVisualPosition(Vector3 position)
{
transform.position = position;
}
#region Feature
@@ -206,8 +154,7 @@ namespace NBF
t.OnLineBreakRequested();
}
}
private void EnsureFeatureCache()
{
@@ -293,13 +240,6 @@ namespace NBF
private void OnDrawGizmos()
{
if (!drawDebugGizmo)
{
return;
}
Gizmos.color = runtimeVirtualNode ? virtualNodeColor : logicalNodeColor;
Gizmos.DrawSphere(transform.position, debugGizmoRadius);
}
}
}

View File

@@ -1,630 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace NBF
{
[RequireComponent(typeof(LineRenderer))]
public class FishingLineRenderer : MonoBehaviour
{
[Header("References")]
[SerializeField] private FishingLineSolver solver;
[SerializeField] private LineRenderer lineRenderer;
[Header("Verlet")]
[Min(1)]
[SerializeField] private int solverIterations = 8;
[Range(0f, 1f)]
[SerializeField] private float damping = 0.98f;
[Min(0f)]
[SerializeField] private float gravityScale = 1f;
[Min(0.001f)]
[SerializeField] private float simulationStep = 0.0166667f;
[Min(0.001f)]
[SerializeField] private float maxDeltaTime = 0.0333333f;
[Header("Water Surface")]
[SerializeField] private bool constrainToWaterSurface = true;
[SerializeField] private Transform waterSurfaceTransform;
[SerializeField] private float waterSurfaceHeight;
[Min(0)]
[SerializeField] private int ignoreHeadNodeCount = 1;
[Min(0)]
[SerializeField] private int ignoreTailNodeCount = 1;
[Min(0f)]
[SerializeField] private float waterSurfaceFollowSpeed = 12f;
[Header("Stability")]
[Min(1)]
[SerializeField] private int maxSubStepsPerFrame = 2;
[Min(0f)]
[SerializeField] private float sleepVelocityThreshold = 0.001f;
[Min(0f)]
[SerializeField] private float sleepDistanceThreshold = 0.002f;
[Min(1)]
[SerializeField] private int stableFramesBeforeSleep = 4;
[Min(0f)]
[SerializeField] private float wakeDistanceThreshold = 0.001f;
[Header("Corner Smoothing")]
[SerializeField] private bool smoothCorners = true;
[Range(0f, 180f)]
[SerializeField] private float minCornerAngle = 12f;
[Min(0f)]
[SerializeField] private float maxCornerSmoothDistance = 0.03f;
[Min(1)]
[SerializeField] private int cornerSmoothSubdivisions = 3;
[Header("Debug")]
[SerializeField] private bool drawDebugSamples;
[SerializeField] private Color debugLogicalSampleColor = Color.cyan;
[SerializeField] private Color debugVirtualSampleColor = new(1f, 0.55f, 0.15f, 1f);
[Min(0.001f)]
[SerializeField] private float debugLogicalSampleRadius = 0.018f;
[Min(0.001f)]
[SerializeField] private float debugVirtualSampleRadius = 0.012f;
private readonly List<Vector3> positions = new();
private readonly List<Vector3> renderPositions = new();
private readonly List<Vector3> previousPositions = new();
private readonly List<long> sampledPointKeys = new();
private readonly List<Vector3> lastPinnedPointPositions = new();
private readonly List<float> lastRestLengths = new();
private bool[] pinnedFlags = System.Array.Empty<bool>();
private float accumulatedTime;
private bool isSleeping;
private int stableFrameCounter;
public int SampleCount => positions.Count;
public float CurrentRenderedLength
{
get
{
var total = 0f;
var source = renderPositions.Count > 0 ? renderPositions : positions;
for (var i = 0; i < source.Count - 1; i++)
{
total += Vector3.Distance(source[i], source[i + 1]);
}
return total;
}
}
private void Reset()
{
TryGetComponent(out lineRenderer);
if (solver == null)
{
TryGetComponent(out solver);
}
}
private void Awake()
{
if (lineRenderer == null)
{
TryGetComponent(out lineRenderer);
}
}
public void Render(FishingLineSolver sourceSolver, float deltaTime)
{
if (lineRenderer == null)
{
return;
}
solver = sourceSolver;
var points = solver.ChainPoints;
var restLengths = solver.RestLengths;
var pinnedIndices = solver.PinnedIndices;
if (points.Count == 0)
{
lineRenderer.positionCount = 0;
return;
}
var topologyChanged = EnsureBuffers(points, pinnedIndices);
if (topologyChanged || ShouldWake(points, restLengths))
{
WakeUp();
}
Simulate(points, restLengths, deltaTime);
ApplyToRenderer();
CacheFrameState(points, restLengths);
}
private bool EnsureBuffers(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<int> pinnedIndices)
{
var topologyChanged = sampledPointKeys.Count != points.Count;
if (!topologyChanged)
{
for (var i = 0; i < points.Count; i++)
{
if (sampledPointKeys[i] == points[i].Key)
{
continue;
}
topologyChanged = true;
break;
}
}
var previousPositionMap = new Dictionary<long, Vector3>(sampledPointKeys.Count);
var previousHistoryMap = new Dictionary<long, Vector3>(sampledPointKeys.Count);
for (var i = 0; i < sampledPointKeys.Count; i++)
{
previousPositionMap[sampledPointKeys[i]] = positions[i];
previousHistoryMap[sampledPointKeys[i]] = previousPositions[i];
}
positions.Clear();
previousPositions.Clear();
sampledPointKeys.Clear();
pinnedFlags = new bool[points.Count];
for (var i = 0; i < points.Count; i++)
{
var point = points[i];
sampledPointKeys.Add(point.Key);
if (previousPositionMap.TryGetValue(point.Key, out var preservedPosition))
{
positions.Add(preservedPosition);
previousPositions.Add(previousHistoryMap[point.Key]);
continue;
}
positions.Add(point.Position);
previousPositions.Add(point.Position);
}
for (var i = 0; i < pinnedIndices.Count; i++)
{
var pinnedIndex = pinnedIndices[i];
if (pinnedIndex >= 0 && pinnedIndex < pinnedFlags.Length)
{
pinnedFlags[pinnedIndex] = true;
}
}
return topologyChanged;
}
private void Simulate(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths,
float deltaTime)
{
if (isSleeping)
{
PinLogicalPoints(points);
return;
}
var clampedDelta = Mathf.Clamp(deltaTime, 0f, maxDeltaTime);
accumulatedTime = Mathf.Min(accumulatedTime + clampedDelta, simulationStep * maxSubStepsPerFrame);
var subStepCount = 0;
while (accumulatedTime >= simulationStep && subStepCount < maxSubStepsPerFrame)
{
SimulateStep(points, restLengths, simulationStep);
accumulatedTime -= simulationStep;
subStepCount++;
}
if (subStepCount == 0)
{
PinLogicalPoints(points);
ApplySleep();
}
EvaluateSleepState(restLengths);
}
private void SimulateStep(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths,
float stepDelta)
{
var gravity = Physics.gravity * gravityScale * stepDelta * stepDelta;
for (var i = 0; i < points.Count; i++)
{
if (pinnedFlags[i])
{
positions[i] = points[i].Position;
previousPositions[i] = points[i].Position;
continue;
}
var current = positions[i];
var velocity = (current - previousPositions[i]) * damping;
previousPositions[i] = current;
positions[i] = current + velocity + gravity;
}
SolveDistanceConstraints(points, restLengths);
ApplyWaterSurfaceConstraint(stepDelta);
SolveDistanceConstraints(points, restLengths);
// Keep a final pure distance solve so the render chain settles back to its rest-length budget
// without reintroducing the old forced-straightening behavior.
SolveDistanceConstraints(points, restLengths);
PinLogicalPoints(points);
ApplySleep();
}
private void SolveDistanceConstraints(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths)
{
for (var iteration = 0; iteration < solverIterations; iteration++)
{
PinLogicalPoints(points);
for (var segmentIndex = 0; segmentIndex < restLengths.Count; segmentIndex++)
{
SatisfyDistanceConstraint(segmentIndex, restLengths[segmentIndex]);
}
}
}
private void PinLogicalPoints(IReadOnlyList<FishingLineSolver.ChainPoint> points)
{
for (var i = 0; i < points.Count; i++)
{
if (!pinnedFlags[i])
{
continue;
}
positions[i] = points[i].Position;
previousPositions[i] = points[i].Position;
}
}
private void SatisfyDistanceConstraint(int segmentIndex, float restLength)
{
var pointA = positions[segmentIndex];
var pointB = positions[segmentIndex + 1];
var delta = pointB - pointA;
var distance = delta.magnitude;
if (distance <= 0.0001f)
{
return;
}
var correctionScale = (distance - restLength) / distance;
if (Mathf.Approximately(correctionScale, 0f))
{
return;
}
var pointAPinned = pinnedFlags[segmentIndex];
var pointBPinned = pinnedFlags[segmentIndex + 1];
if (pointAPinned && pointBPinned)
{
return;
}
if (pointAPinned)
{
positions[segmentIndex + 1] -= delta * correctionScale;
return;
}
if (pointBPinned)
{
positions[segmentIndex] += delta * correctionScale;
return;
}
var correction = delta * (correctionScale * 0.5f);
positions[segmentIndex] += correction;
positions[segmentIndex + 1] -= correction;
}
private void ApplyWaterSurfaceConstraint(float stepDelta)
{
if (!constrainToWaterSurface || positions.Count == 0)
{
return;
}
var surfaceHeight = waterSurfaceTransform != null ? waterSurfaceTransform.position.y : waterSurfaceHeight;
var startIndex = Mathf.Clamp(ignoreHeadNodeCount, 0, positions.Count);
var endExclusive = Mathf.Clamp(positions.Count - ignoreTailNodeCount, startIndex, positions.Count);
var followFactor = Mathf.Clamp01(waterSurfaceFollowSpeed * stepDelta);
for (var i = startIndex; i < endExclusive; i++)
{
if (pinnedFlags[i])
{
continue;
}
var current = positions[i];
if (current.y >= surfaceHeight)
{
continue;
}
var nextY = Mathf.Lerp(current.y, surfaceHeight, followFactor);
positions[i] = new Vector3(current.x, nextY, current.z);
var previous = previousPositions[i];
previousPositions[i] = new Vector3(
previous.x,
Mathf.Lerp(previous.y, nextY, followFactor),
previous.z);
}
}
private void ApplySleep()
{
for (var i = 0; i < positions.Count; i++)
{
if (pinnedFlags[i])
{
continue;
}
var velocityMagnitude = (positions[i] - previousPositions[i]).magnitude;
if (velocityMagnitude <= sleepVelocityThreshold)
{
previousPositions[i] = positions[i];
}
}
}
private void EvaluateSleepState(IReadOnlyList<float> restLengths)
{
var isStable = true;
for (var i = 0; i < positions.Count; i++)
{
if (pinnedFlags[i])
{
continue;
}
if ((positions[i] - previousPositions[i]).magnitude > sleepVelocityThreshold)
{
isStable = false;
break;
}
}
if (isStable)
{
for (var i = 0; i < restLengths.Count; i++)
{
var error = Mathf.Abs(Vector3.Distance(positions[i], positions[i + 1]) - restLengths[i]);
if (error > sleepDistanceThreshold)
{
isStable = false;
break;
}
}
}
if (!isStable)
{
stableFrameCounter = 0;
return;
}
stableFrameCounter++;
if (stableFrameCounter < stableFramesBeforeSleep)
{
return;
}
isSleeping = true;
accumulatedTime = 0f;
for (var i = 0; i < positions.Count; i++)
{
previousPositions[i] = positions[i];
}
}
private bool ShouldWake(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths)
{
if (!isSleeping)
{
return false;
}
if (lastPinnedPointPositions.Count != points.Count || lastRestLengths.Count != restLengths.Count)
{
return true;
}
for (var i = 0; i < points.Count; i++)
{
if (!pinnedFlags[i])
{
continue;
}
if (Vector3.Distance(points[i].Position, lastPinnedPointPositions[i]) > wakeDistanceThreshold)
{
return true;
}
}
for (var i = 0; i < restLengths.Count; i++)
{
if (Mathf.Abs(restLengths[i] - lastRestLengths[i]) > wakeDistanceThreshold)
{
return true;
}
}
return false;
}
private void CacheFrameState(
IReadOnlyList<FishingLineSolver.ChainPoint> points,
IReadOnlyList<float> restLengths)
{
lastPinnedPointPositions.Clear();
for (var i = 0; i < points.Count; i++)
{
lastPinnedPointPositions.Add(points[i].Position);
}
lastRestLengths.Clear();
for (var i = 0; i < restLengths.Count; i++)
{
lastRestLengths.Add(restLengths[i]);
}
}
private void WakeUp()
{
isSleeping = false;
stableFrameCounter = 0;
accumulatedTime = 0f;
}
private void ApplyToRenderer()
{
BuildRenderPositions();
lineRenderer.positionCount = renderPositions.Count;
for (var i = 0; i < renderPositions.Count; i++)
{
lineRenderer.SetPosition(i, renderPositions[i]);
}
}
private void BuildRenderPositions()
{
renderPositions.Clear();
if (positions.Count == 0)
{
return;
}
if (!smoothCorners || positions.Count < 3)
{
renderPositions.AddRange(positions);
return;
}
renderPositions.Add(positions[0]);
for (var i = 1; i < positions.Count - 1; i++)
{
var previous = positions[i - 1];
var current = positions[i];
var next = positions[i + 1];
if (!TryBuildSmoothedCorner(previous, current, next, out var entry, out var exit))
{
AddRenderPointIfDistinct(current);
continue;
}
AddRenderPointIfDistinct(entry);
for (var subdivision = 1; subdivision <= cornerSmoothSubdivisions; subdivision++)
{
var t = subdivision / (cornerSmoothSubdivisions + 1f);
var pointOnCurve = EvaluateQuadraticBezier(entry, current, exit, t);
AddRenderPointIfDistinct(pointOnCurve);
}
AddRenderPointIfDistinct(exit);
}
AddRenderPointIfDistinct(positions[^1]);
}
private bool TryBuildSmoothedCorner(
Vector3 previous,
Vector3 current,
Vector3 next,
out Vector3 entry,
out Vector3 exit)
{
entry = current;
exit = current;
var incoming = current - previous;
var outgoing = next - current;
var incomingLength = incoming.magnitude;
var outgoingLength = outgoing.magnitude;
if (incomingLength <= 0.0001f || outgoingLength <= 0.0001f)
{
return false;
}
var cornerAngle = Vector3.Angle(incoming, outgoing);
if (cornerAngle < minCornerAngle)
{
return false;
}
var trimDistance = Mathf.Min(
maxCornerSmoothDistance,
incomingLength * 0.5f,
outgoingLength * 0.5f);
if (trimDistance <= 0.0001f)
{
return false;
}
entry = current - (incoming / incomingLength) * trimDistance;
exit = current + (outgoing / outgoingLength) * trimDistance;
return true;
}
private void AddRenderPointIfDistinct(Vector3 point)
{
if (renderPositions.Count > 0 && Vector3.Distance(renderPositions[^1], point) <= 0.0001f)
{
return;
}
renderPositions.Add(point);
}
private static Vector3 EvaluateQuadraticBezier(Vector3 start, Vector3 control, Vector3 end, float t)
{
var oneMinusT = 1f - t;
return (oneMinusT * oneMinusT * start)
+ (2f * oneMinusT * t * control)
+ (t * t * end);
}
private void OnDrawGizmosSelected()
{
if (!drawDebugSamples || positions.Count == 0)
{
return;
}
for (var i = 0; i < positions.Count; i++)
{
var isLogicalPoint = solver != null
&& solver.ChainPoints != null
&& i < solver.ChainPoints.Count
&& solver.ChainPoints[i].IsLogical;
Gizmos.color = isLogicalPoint ? debugLogicalSampleColor : debugVirtualSampleColor;
Gizmos.DrawSphere(
positions[i],
isLogicalPoint ? debugLogicalSampleRadius : debugVirtualSampleRadius);
}
}
}
}

View File

@@ -17,15 +17,87 @@ namespace NBF
{
[SerializeField] public LineType LineType;
[Header("References")] [SerializeField]
private Transform anchorTransform;
[SerializeField] private FishingLineNode[] logicalNodes = Array.Empty<FishingLineNode>();
public JointPinchController PinchController;
private void FixedUpdate()
{
UpdateAnchorNode();
CalculateLineRealLength(Time.fixedDeltaTime);
}
#region Start Node
private void ConfigureStartNode()
{
if (logicalNodes == null || logicalNodes.Length == 0 || logicalNodes[0] == null)
{
return;
}
var startNode = logicalNodes[0];
startNode.Type = FishingLineNode.NodeType.Start;
if (startNode.Body != null)
{
startNode.Body.isKinematic = true;
startNode.Body.interpolation = RigidbodyInterpolation.Interpolate;
startNode.Body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
}
UpdateAnchorNode();
}
private void UpdateAnchorNode()
{
if (anchorTransform == null || logicalNodes == null || logicalNodes.Length == 0 || logicalNodes[0] == null)
{
return;
}
var startNode = logicalNodes[0];
startNode.transform.SetPositionAndRotation(anchorTransform.position, anchorTransform.rotation);
if (startNode.Body != null)
{
if (!startNode.Body.isKinematic)
{
startNode.Body.linearVelocity = Vector3.zero;
startNode.Body.angularVelocity = Vector3.zero;
}
}
}
#endregion
#region Line
/// <summary>
/// 当前逻辑链总长度超出配置总长度的部分,小于等于零时记为 0。
/// </summary>
public float CurrentStretchLength { get; private set; }
/// <summary>
/// 设置指定逻辑段的配置长度。
/// segmentIndex 为 0 时表示第一段;大于 0 时表示对应逻辑节点到下一个逻辑节点的线长。
/// </summary>
public void SetLenght(float length, int segmentIndex = 0)
{
ConfigureStartNode();
CalculateLineRealLength(0f);
}
private void CalculateLineRealLength(float deltaTime)
{
}
#endregion
#region LineNode
/// <summary>
@@ -34,6 +106,23 @@ namespace NBF
/// </summary>
public IReadOnlyList<FishingLineNode> LogicalNodes => logicalNodes;
/// <summary>
/// 根据类型获取逻辑节点类型
/// </summary>
/// <param name="nodeType"></param>
/// <returns></returns>
public FishingLineNode GetLogicalNode(FishingLineNode.NodeType nodeType)
{
foreach (var fishingLineNode in logicalNodes)
{
if (fishingLineNode.Type == nodeType)
{
return fishingLineNode;
}
}
return null;
}
/// <summary>
/// 获取指定顺序索引的逻辑节点。
@@ -105,6 +194,62 @@ namespace NBF
#endregion
#region
[Header("Limit Detection")]
[Min(0f)]
// 极限判定的长度容差,允许链路在总长或单段长度上存在少量误差。
[SerializeField]
private float lengthLimitTolerance = 0.01f;
[Min(0f)]
// 达到极限后,只有当前超长值大于该阈值时,才开始进入断线候选计时。
[SerializeField]
private float breakStretchThreshold = 0.05f;
/// <summary>
/// 当鱼线达到断线条件时发出的一次性消息。
/// 外部可订阅该事件,在回调中执行切线、播放表现或状态切换。
/// </summary>
public event Action<FishingLineSolver> OnLineBreakRequested;
/// <summary>
/// 当前断线候选状态的累计时间。
/// 只有在处于极限状态,且 CurrentStretchLength 大于断线阈值时才会累加;否则重置为 0。
/// </summary>
public float LimitStateTime { get; private set; }
/// <summary>
/// 当前拉力极限百分比。
/// 当超长值小于等于 lengthLimitTolerance 时为 0
/// 当超长值大于等于 breakStretchThreshold 时为 100
/// 中间区间按线性比例映射,供 UI 显示使用。
/// </summary>
public float CurrentBreakStretchPercent => EvaluateBreakStretchPercent(CurrentStretchLength);
private float EvaluateBreakStretchPercent(float stretchLength)
{
if (stretchLength <= lengthLimitTolerance)
{
return 0f;
}
if (stretchLength >= breakStretchThreshold)
{
return 100f;
}
if (breakStretchThreshold <= lengthLimitTolerance)
{
return 100f;
}
return Mathf.InverseLerp(lengthLimitTolerance, breakStretchThreshold, stretchLength) * 100f;
}
#endregion
protected override void OnInit()
{
var tipRb = Rod.Asset.LineConnectorRigidbody;

View File

@@ -37,15 +37,14 @@ namespace NBF
targetFirstSegmentLength =
Mathf.Clamp(initialFirstSegmentLength, minFirstSegmentLength, maxFirstSegmentLength);
solver.SetLenght(targetFirstSegmentLength);
solver.BuildLine();
// solver.BuildLine();
solver.OnLineBreakRequested += OnLineBreakRequested;
}
private void OnLineBreakRequested(FishingLineSolver lineSolver)
{
Debug.LogError(
$"当前拉力达到极限,切线,极限时间={lineSolver.LimitStateTime} CurrentStretchLength={lineSolver.CurrentStretchLength} CurrentTensionRatio={lineSolver.CurrentTensionRatio}");
Debug.LogError($"当前拉力达到极限,切线,极限时间={lineSolver.LimitStateTime}");
var endNode = lineSolver.GetEndNode();
if (endNode != null)
{
@@ -83,21 +82,6 @@ namespace NBF
{
// Debug.LogError(solver.CurrentBreakStretchPercent);
}
// if (solver.IsAtLimit)
// {
// if (solver.CurrentStretchLength > 0.04)
// Debug.LogError($"CurrentStretchLength={solver.CurrentStretchLength}");
// if (solver.CurrentStretchLength > 0.1 && solver.LimitStateTime > 2f)
// {
// Debug.LogError(
// $"当前拉力达到极限,切线,极限时间={solver.LimitStateTime} CurrentStretchLength={solver.CurrentStretchLength} CurrentTensionRatio={solver.CurrentTensionRatio}");
// var endNode = solver.GetEndNode();
// if (endNode != null)
// {
// endNode.Body.isKinematic = false;
// }
// }
// }
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 38f37be41b0a4792b866b9ad9b4d9268
timeCreated: 1776093454

View File

@@ -0,0 +1,19 @@
using UnityEngine;
namespace NBF
{
[RequireComponent(typeof(LineRenderer))]
public class FishingLineRenderer : MonoBehaviour
{
[Header("References")] [SerializeField]
private FishingLineSolver solver;
[SerializeField] private LineRenderer lineRenderer;
private void Awake()
{
lineRenderer = GetComponent<LineRenderer>();
solver = GetComponent<FishingLineSolver>();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 98ba9d435a0e49c9bb527c34cc91894d
timeCreated: 1766759607