From caa260b53b124a2852441c877f0e25a8568c57df Mon Sep 17 00:00:00 2001 From: "Bob.Song" <605277374@qq.com> Date: Mon, 13 Apr 2026 20:55:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BA=BF=E6=B1=82=E8=A7=A3?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../New/View/FishingLine/FishingLineSolver.cs | 877 +----------------- 1 file changed, 14 insertions(+), 863 deletions(-) diff --git a/Assets/Scripts/Fishing/New/View/FishingLine/FishingLineSolver.cs b/Assets/Scripts/Fishing/New/View/FishingLine/FishingLineSolver.cs index 9502aca24..73418f7bc 100644 --- a/Assets/Scripts/Fishing/New/View/FishingLine/FishingLineSolver.cs +++ b/Assets/Scripts/Fishing/New/View/FishingLine/FishingLineSolver.cs @@ -15,93 +15,18 @@ namespace NBF public class FishingLineSolver : FGearBase { - [Serializable] - public sealed class ChainPoint - { - public long Key; - public Vector3 Position; - public bool IsLogical; - public FishingLineNode LogicalNode; - public int SegmentIndex; - public int StableIndex; - - public string GetDebugName() - { - if (IsLogical) - { - return LogicalNode != null ? LogicalNode.GetDebugName() : $"L[{StableIndex}]"; - } - - return $"V[S{SegmentIndex}:{StableIndex}]"; - } - } - - [Serializable] - private struct SegmentLayout - { - public float[] GapLengths; - - public int VirtualNodeCount => Mathf.Max(0, GapLengths.Length - 1); - } - - [SerializeField] public LineType LineType; - public JointPinchController PinchController; + [Header("References")] [SerializeField] private Transform anchorTransform; - + [SerializeField] private FishingLineNode[] logicalNodes = Array.Empty(); - [SerializeField] private FishingLineRenderer lineRenderer; - [Header("First Segment")] [Min(0f)] [SerializeField] - private float firstSegmentLength = 1.2f; + public JointPinchController PinchController; - [Min(0.001f)] [SerializeField] private float firstSegmentStep = 0.1f; - - [Header("Joint")] [Min(1)] [SerializeField] - private int jointSolverIterations = 12; - - [SerializeField] private float jointProjectionDistance = 0.02f; - [SerializeField] private float jointProjectionAngle = 1f; - - [Header("Limit Detection")] - [Min(0f)] - // 极限判定的长度容差,允许链路在总长或单段长度上存在少量误差。 - [SerializeField] - private float lengthLimitTolerance = 0.01f; - - [Min(0f)] - // 达到极限后,只有当前超长值大于该阈值时,才开始进入断线候选计时。 - [SerializeField] - private float breakStretchThreshold = 0.05f; - - [Min(0f)] - // 断线候选状态允许持续的最大时间;超过后会发出一次断线消息。 - [SerializeField] - private float breakLimitDuration = 0.15f; - - [Header("Runtime Debug")] [SerializeField] - private bool autoBuildOnStart = true; - - private readonly List chainPoints = new(); - private readonly List restLengths = new(); - private readonly List pinnedIndices = new(); - private readonly List runtimeJoints = new(); - - private bool chainDirty = true; - private int runtimeVirtualPointCount; - - public float FirstSegmentLength => firstSegmentLength; - - public float FirstSegmentStep => firstSegmentStep; - - public IReadOnlyList ChainPoints => chainPoints; - - public IReadOnlyList RestLengths => restLengths; - - public IReadOnlyList PinnedIndices => pinnedIndices; + #region LineNode /// /// 当前配置的逻辑节点只读列表。 @@ -109,269 +34,21 @@ namespace NBF /// public IReadOnlyList LogicalNodes => logicalNodes; - /// - /// 当前整条鱼线的配置总长度,等于所有段的静止长度之和。 - /// - public float TotalLineLength { get; private set; } /// - /// 当前逻辑节点链路的实际总长度,按相邻逻辑节点的实际距离累加。 + /// 获取指定顺序索引的逻辑节点。 + /// 索引基于 logicalNodes 配置顺序;超出范围或节点为空时返回 null。 /// - public float CurrentLogicalChainLength { get; private set; } - - /// - /// 当前首尾逻辑节点之间的直线距离,仅作为端点跨度观察值。 - /// - public float CurrentEndpointDistance { get; private set; } - - /// - /// 当前逻辑链总长度超出配置总长度的部分,小于等于零时记为 0。 - /// - public float CurrentStretchLength { get; private set; } - - /// - /// 当前所有逻辑段中,单段超出配置长度的最大值。 - /// - public float MaxSegmentStretchLength { get; private set; } - - /// - /// 当前超限最明显的逻辑段索引;为 -1 表示没有段处于超限。 - /// - public int MaxOverstretchedSegmentIndex { get; private set; } = -1; - - /// - /// 当前受拉比例。 - /// 该值取“单段实际长度 / 单段配置长度”和“整链实际长度 / 整链配置长度”中的最大值。 - /// 约等于 1 表示接近拉直,大于 1 表示已经出现超限拉伸。 - /// - public float CurrentTensionRatio { get; private set; } - - /// - /// 断线候选拉伸阈值。 - /// 只有当前处于极限状态,且超长值大于该阈值时,才会开始累计断线计时。 - /// - public float BreakStretchThreshold => breakStretchThreshold; - - /// - /// 断线候选状态可持续的最大时间。 - /// 当断线计时超过该值时,会发出一次断线消息。 - /// - public float BreakLimitDuration => breakLimitDuration; - - /// - /// 当前拉力极限百分比。 - /// 当超长值小于等于 lengthLimitTolerance 时为 0; - /// 当超长值大于等于 breakStretchThreshold 时为 100; - /// 中间区间按线性比例映射,供 UI 显示使用。 - /// - public float CurrentBreakStretchPercent => EvaluateBreakStretchPercent(CurrentStretchLength); - - /// - /// 当前是否处于极限状态。 - /// 只要整链超出总长度容差,或任一逻辑段超出单段容差,即认为到达极限。 - /// - public bool IsAtLimit { get; private set; } - - /// - /// 当前断线候选状态的累计时间。 - /// 只有在处于极限状态,且 CurrentStretchLength 大于断线阈值时才会累加;否则重置为 0。 - /// - public float LimitStateTime { get; private set; } - - /// - /// 当前是否正在进行断线候选计时。 - /// - public bool IsBreakCountdownActive => IsAtLimit && CurrentStretchLength > breakStretchThreshold; - - /// - /// 当前极限断线消息是否已经发出过。 - /// 在退出断线候选状态前只会发一次,避免重复通知。 - /// - public bool HasBreakNotificationSent { get; private set; } - - /// - /// 当鱼线达到断线条件时发出的一次性消息。 - /// 外部可订阅该事件,在回调中执行切线、播放表现或状态切换。 - /// - public event Action OnLineBreakRequested; - - public int LogicalNodeCount => logicalNodes?.Length ?? 0; - - public int RuntimeVirtualNodeCount => runtimeVirtualPointCount; - - public int ActiveRuntimeVirtualNodeCount => runtimeVirtualPointCount; - - public int OrderedNodeCount => chainPoints.Count; - - public int SegmentCount => restLengths.Count; - - public bool IsChainDirty => chainDirty; - - private void Reset() + public FishingLineNode GetLogicalNode(int logicalIndex) { - if (lineRenderer == null) + if (logicalNodes == null || logicalIndex < 0 || logicalIndex >= logicalNodes.Length) { - TryGetComponent(out lineRenderer); - } - } - - protected override void OnInit() - { - var tipRb = Rod.Asset.LineConnectorRigidbody; - anchorTransform = tipRb.transform; - } - - private void Start() - { - if (autoBuildOnStart) - { - BuildLine(); - } - } - - private void FixedUpdate() - { - if (logicalNodes == null || logicalNodes.Length == 0) - { - ResetLimitState(); - return; + return null; } - UpdateAnchorNode(); - - if (chainDirty) - { - RebuildRuntimeChain(); - } - - EvaluateLimitState(Time.fixedDeltaTime); + return logicalNodes[logicalIndex]; } - private void LateUpdate() - { - if (chainDirty) - { - RebuildRuntimeChain(); - } - - SyncLogicalPointPositions(); - - if (lineRenderer != null && chainPoints.Count > 1) - { - lineRenderer.Render(this, Time.deltaTime); - } - } - - private void OnValidate() - { - firstSegmentLength = Mathf.Max(0f, firstSegmentLength); - firstSegmentStep = Mathf.Max(0.001f, firstSegmentStep); - jointSolverIterations = Mathf.Max(1, jointSolverIterations); - lengthLimitTolerance = Mathf.Max(0f, lengthLimitTolerance); - breakStretchThreshold = Mathf.Max(0f, breakStretchThreshold); - breakLimitDuration = Mathf.Max(0f, breakLimitDuration); - chainDirty = true; - } - - /// - /// 按当前配置重建整条鱼线的运行时链路,并立即刷新极限状态。 - /// - [ContextMenu("Build Line")] - public void BuildLine() - { - ConfigureStartNode(); - ConfigureLogicalJoints(); - RebuildRuntimeChain(); - EvaluateLimitState(0f); - } - - /// - /// 设置指定逻辑段的配置长度。 - /// segmentIndex 为 0 时表示第一段;大于 0 时表示对应逻辑节点到下一个逻辑节点的线长。 - /// - public void SetLenght(float length, int segmentIndex = 0) - { - var clamped = Mathf.Max(0f, length); - var currentLength = GetSegmentLength(segmentIndex); - if (Mathf.Approximately(clamped, currentLength)) - { - return; - } - - if (segmentIndex <= 0) - { - firstSegmentLength = clamped; - } - else - { - if (logicalNodes == null || segmentIndex >= logicalNodes.Length) - { - return; - } - - var sourceNode = logicalNodes[segmentIndex]; - if (sourceNode == null) - { - return; - } - - sourceNode.SegmentLengthToNext = clamped; - } - - UpdateJointLimit(segmentIndex + 1, clamped); - chainDirty = true; - } - - /// - /// 立即按当前配置重建鱼线。 - /// - public void RebuildNow() - { - BuildLine(); - } - - /// - /// 根据类型获取逻辑节点类型 - /// - /// - /// - public FishingLineNode GetLogicalNode(FishingLineNode.NodeType nodeType) - { - foreach (var fishingLineNode in logicalNodes) - { - if (fishingLineNode.Type == nodeType) - { - return fishingLineNode; - } - } - - return null; - } - - // /// - // /// 获取指定顺序索引的逻辑节点。 - // /// 索引基于 logicalNodes 配置顺序;超出范围或节点为空时返回 null。 - // /// - // public FishingLineNode GetLogicalNode(int logicalIndex) - // { - // if (logicalNodes == null || logicalIndex < 0 || logicalIndex >= logicalNodes.Length) - // { - // return null; - // } - // - // return logicalNodes[logicalIndex]; - // } - // - // /// - // /// 尝试获取指定顺序索引的逻辑节点。 - // /// 获取失败时返回 false,并将 node 置为 null。 - // /// - // public bool TryGetLogicalNode(int logicalIndex, out FishingLineNode node) - // { - // node = GetLogicalNode(logicalIndex); - // return node != null; - // } - /// /// 获取当前起点逻辑节点。 /// 会返回配置顺序中第一个非空节点。 @@ -390,530 +67,6 @@ namespace NBF return FindLastValidLogicalNode(); } - /// - /// 获取当前运行时链路中相邻采样点的实际距离。 - /// 这里的采样点包含逻辑节点和虚拟节点。 - /// - public float GetActualDistance(int segmentIndex) - { - if (segmentIndex < 0 || segmentIndex >= chainPoints.Count - 1) - { - return 0f; - } - - return Vector3.Distance(chainPoints[segmentIndex].Position, chainPoints[segmentIndex + 1].Position); - } - - /// - /// 返回当前鱼线运行时调试摘要,包含链路结构、长度和极限状态信息。 - /// - public string GetRuntimeDebugSummary() - { - var builder = new StringBuilder(512); - builder.Append("chain:") - .Append(chainDirty ? "dirty" : "ready") - .Append(" logical:") - .Append(LogicalNodeCount) - .Append(" runtimeVirtual:") - .Append(RuntimeVirtualNodeCount) - .Append(" ordered:") - .Append(OrderedNodeCount) - .Append(" total:") - .Append(TotalLineLength.ToString("F2")) - .Append("m") - .Append(" chain:") - .Append(CurrentLogicalChainLength.ToString("F2")) - .Append("m") - .Append(" tension:") - .Append(CurrentTensionRatio.ToString("F3")) - .Append(" breakPercent:") - .Append(CurrentBreakStretchPercent.ToString("F1")) - .Append('%') - .Append(" limit:") - .Append(IsAtLimit ? "yes" : "no") - .Append(" limitTime:") - .Append(LimitStateTime.ToString("F2")) - .Append("s"); - - for (var i = 0; i < chainPoints.Count; i++) - { - var point = chainPoints[i]; - builder.AppendLine() - .Append('#') - .Append(i) - .Append(' ') - .Append(point.GetDebugName()) - .Append(" pos:") - .Append(point.Position); - - if (point.IsLogical && point.LogicalNode != null) - { - builder.Append(" body:") - .Append(point.LogicalNode.Body != null ? "yes" : "no"); - } - - if (i < restLengths.Count) - { - builder.Append(" seg rest:") - .Append(restLengths[i].ToString("F3")) - .Append(" actual:") - .Append(GetActualDistance(i).ToString("F3")); - } - } - - return builder.ToString(); - } - - 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 ConfigureLogicalJoints() - { - runtimeJoints.Clear(); - - if (logicalNodes == null) - { - return; - } - - for (var i = 1; i < logicalNodes.Length; i++) - { - var current = logicalNodes[i]; - var previous = logicalNodes[i - 1]; - if (current == null || previous == null || current.Body == null || previous.Body == null) - { - continue; - } - - current.Body.solverIterations = jointSolverIterations; - current.Body.interpolation = RigidbodyInterpolation.Interpolate; - current.Body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic; - - var joint = current.GetComponent(); - if (joint == null) - { - joint = current.gameObject.AddComponent(); - } - - joint.autoConfigureConnectedAnchor = true; - joint.connectedBody = previous.Body; - joint.xMotion = ConfigurableJointMotion.Limited; - joint.yMotion = ConfigurableJointMotion.Limited; - joint.zMotion = ConfigurableJointMotion.Limited; - joint.angularXMotion = ConfigurableJointMotion.Free; - joint.angularYMotion = ConfigurableJointMotion.Free; - joint.angularZMotion = ConfigurableJointMotion.Free; - joint.projectionMode = JointProjectionMode.PositionAndRotation; - joint.projectionDistance = jointProjectionDistance; - joint.projectionAngle = jointProjectionAngle; - - var limit = joint.linearLimit; - limit.limit = GetSegmentLength(i - 1); - joint.linearLimit = limit; - - runtimeJoints.Add(joint); - } - } - - 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; - } - } - } - - private void EvaluateLimitState(float deltaTime) - { - CurrentLogicalChainLength = 0f; - CurrentEndpointDistance = 0f; - CurrentStretchLength = 0f; - MaxSegmentStretchLength = 0f; - MaxOverstretchedSegmentIndex = -1; - CurrentTensionRatio = 0f; - - if (logicalNodes == null || logicalNodes.Length < 2) - { - SetLimitState(false); - UpdateBreakCountdown(deltaTime); - return; - } - - FishingLineNode firstNode = null; - FishingLineNode lastNode = null; - - for (var segmentIndex = 0; segmentIndex < logicalNodes.Length; segmentIndex++) - { - var node = logicalNodes[segmentIndex]; - if (node == null) - { - continue; - } - - firstNode ??= node; - lastNode = node; - - if (segmentIndex >= logicalNodes.Length - 1) - { - continue; - } - - var nextNode = logicalNodes[segmentIndex + 1]; - if (nextNode == null) - { - continue; - } - - var actualDistance = Vector3.Distance(node.Position, nextNode.Position); - var configuredLength = GetSegmentLength(segmentIndex); - CurrentLogicalChainLength += actualDistance; - - if (configuredLength > 0.0001f) - { - CurrentTensionRatio = Mathf.Max(CurrentTensionRatio, actualDistance / configuredLength); - } - - var segmentStretchLength = Mathf.Max(0f, actualDistance - configuredLength); - if (segmentStretchLength > MaxSegmentStretchLength) - { - MaxSegmentStretchLength = segmentStretchLength; - MaxOverstretchedSegmentIndex = segmentStretchLength > 0f ? segmentIndex : -1; - } - } - - if (firstNode != null && lastNode != null && !ReferenceEquals(firstNode, lastNode)) - { - CurrentEndpointDistance = Vector3.Distance(firstNode.Position, lastNode.Position); - } - - if (TotalLineLength > 0.0001f) - { - CurrentStretchLength = Mathf.Max(0f, CurrentLogicalChainLength - TotalLineLength); - CurrentTensionRatio = Mathf.Max(CurrentTensionRatio, CurrentLogicalChainLength / TotalLineLength); - } - else if (CurrentLogicalChainLength > 0f) - { - CurrentStretchLength = CurrentLogicalChainLength; - CurrentTensionRatio = Mathf.Max(CurrentTensionRatio, 1f); - } - - var exceedsTotalLength = CurrentStretchLength > lengthLimitTolerance; - var exceedsSegmentLength = MaxSegmentStretchLength > lengthLimitTolerance; - SetLimitState(exceedsTotalLength || exceedsSegmentLength); - UpdateBreakCountdown(deltaTime); - } - - private void SetLimitState(bool isAtLimit) - { - IsAtLimit = isAtLimit; - } - - private void UpdateBreakCountdown(float deltaTime) - { - if (!IsBreakCountdownActive) - { - LimitStateTime = 0f; - HasBreakNotificationSent = false; - return; - } - - LimitStateTime += Mathf.Max(0f, deltaTime); - - if (HasBreakNotificationSent || LimitStateTime < breakLimitDuration) - { - return; - } - - HasBreakNotificationSent = true; - NotifyLineBreakRequested(); - } - - /// - /// 发出鱼线达到断线条件的消息。 - /// 这里预留给外部订阅,当前不在求解器内部直接执行断线逻辑。 - /// - private void NotifyLineBreakRequested() - { - OnLineBreakRequested?.Invoke(this); - } - - private void ResetLimitState() - { - CurrentLogicalChainLength = 0f; - CurrentEndpointDistance = 0f; - CurrentStretchLength = 0f; - MaxSegmentStretchLength = 0f; - MaxOverstretchedSegmentIndex = -1; - CurrentTensionRatio = 0f; - IsAtLimit = false; - LimitStateTime = 0f; - HasBreakNotificationSent = false; - } - - 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; - } - - private void RebuildRuntimeChain() - { - chainPoints.Clear(); - restLengths.Clear(); - pinnedIndices.Clear(); - TotalLineLength = 0f; - runtimeVirtualPointCount = 0; - - if (logicalNodes == null || logicalNodes.Length < 2) - { - chainDirty = false; - return; - } - - var segmentLayouts = new SegmentLayout[logicalNodes.Length - 1]; - for (var segmentIndex = 0; segmentIndex < segmentLayouts.Length; segmentIndex++) - { - segmentLayouts[segmentIndex] = BuildSegmentLayout(segmentIndex); - } - - AddLogicalPoint(logicalNodes[0], 0); - pinnedIndices.Add(0); - - for (var segmentIndex = 0; segmentIndex < segmentLayouts.Length; segmentIndex++) - { - var fromNode = logicalNodes[segmentIndex]; - var toNode = logicalNodes[segmentIndex + 1]; - if (fromNode == null || toNode == null) - { - continue; - } - - var layout = segmentLayouts[segmentIndex]; - AddVirtualPoints(fromNode.Position, toNode.Position, layout, segmentIndex); - - for (var gapIndex = 0; gapIndex < layout.GapLengths.Length; gapIndex++) - { - restLengths.Add(layout.GapLengths[gapIndex]); - TotalLineLength += layout.GapLengths[gapIndex]; - } - - AddLogicalPoint(toNode, segmentIndex + 1); - pinnedIndices.Add(chainPoints.Count - 1); - } - - chainDirty = false; - } - - private SegmentLayout BuildSegmentLayout(int segmentIndex) - { - var totalLength = GetSegmentLength(segmentIndex); - if (segmentIndex == 0) - { - return new SegmentLayout - { - GapLengths = BuildFirstSegmentGaps(totalLength, firstSegmentStep), - }; - } - - var sourceNode = logicalNodes[segmentIndex]; - var virtualCount = sourceNode != null ? sourceNode.FixedVirtualNodesToNext : 0; - var gapCount = Mathf.Max(1, virtualCount + 1); - var gapLength = totalLength / gapCount; - var gaps = new float[gapCount]; - for (var i = 0; i < gaps.Length; i++) - { - gaps[i] = gapLength; - } - - return new SegmentLayout - { - GapLengths = gaps, - }; - } - - private void AddLogicalPoint(FishingLineNode logicalNode, int logicalIndex) - { - if (logicalNode == null) - { - return; - } - - chainPoints.Add(new ChainPoint - { - Key = BuildLogicalPointKey(logicalIndex), - Position = logicalNode.Position, - IsLogical = true, - LogicalNode = logicalNode, - SegmentIndex = logicalIndex, - StableIndex = logicalIndex, - }); - } - - private void AddVirtualPoints(Vector3 fromPosition, Vector3 toPosition, SegmentLayout layout, int segmentIndex) - { - if (layout.VirtualNodeCount == 0) - { - return; - } - - var direction = toPosition - fromPosition; - var distance = direction.magnitude; - var normalizedDirection = distance > 0.0001f ? direction / distance : Vector3.down; - var accumulatedDistance = 0f; - - for (var virtualIndex = 0; virtualIndex < layout.VirtualNodeCount; virtualIndex++) - { - accumulatedDistance += layout.GapLengths[virtualIndex]; - var stableIndex = segmentIndex == 0 - ? layout.VirtualNodeCount - 1 - virtualIndex - : virtualIndex; - - chainPoints.Add(new ChainPoint - { - Key = BuildVirtualPointKey(segmentIndex, stableIndex), - Position = fromPosition + normalizedDirection * accumulatedDistance, - IsLogical = false, - LogicalNode = null, - SegmentIndex = segmentIndex, - StableIndex = stableIndex, - }); - runtimeVirtualPointCount++; - } - } - - private void SyncLogicalPointPositions() - { - for (var i = 0; i < chainPoints.Count; i++) - { - var point = chainPoints[i]; - if (!point.IsLogical || point.LogicalNode == null) - { - continue; - } - - point.Position = point.LogicalNode.Position; - } - } - - private static float[] BuildFirstSegmentGaps(float totalLength, float step) - { - if (totalLength <= 0f) - { - return new[] { 0f }; - } - - if (totalLength < step) - { - return new[] { totalLength }; - } - - var fullStepCount = Mathf.FloorToInt(totalLength / step); - var remainder = totalLength - (fullStepCount * step); - if (remainder > 0.0001f) - { - var gaps = new float[fullStepCount + 1]; - gaps[0] = remainder; - for (var i = 1; i < gaps.Length; i++) - { - gaps[i] = step; - } - - return gaps; - } - - var divisibleGaps = new float[fullStepCount]; - for (var i = 0; i < divisibleGaps.Length; i++) - { - divisibleGaps[i] = step; - } - - return divisibleGaps; - } - - - private void UpdateJointLimit(int logicalNodeIndex, float limitValue) - { - if (logicalNodeIndex <= 0 || logicalNodeIndex >= logicalNodes.Length) - { - return; - } - - var node = logicalNodes[logicalNodeIndex]; - if (node == null) - { - return; - } - - var joint = node.GetComponent(); - if (joint == null) - { - return; - } - - var limit = joint.linearLimit; - limit.limit = limitValue; - joint.linearLimit = limit; - } - - private float GetSegmentLength(int segmentIndex) - { - if (segmentIndex <= 0) - { - return firstSegmentLength; - } - - if (logicalNodes == null || segmentIndex >= logicalNodes.Length) - { - return 0f; - } - - var sourceNode = logicalNodes[segmentIndex]; - return sourceNode != null ? sourceNode.SegmentLengthToNext : 0f; - } - private FishingLineNode FindFirstValidLogicalNode() { if (logicalNodes == null) @@ -950,14 +103,12 @@ namespace NBF return null; } - private static long BuildLogicalPointKey(int logicalIndex) - { - return (1L << 62) | (uint)logicalIndex; - } + #endregion - private static long BuildVirtualPointKey(int segmentIndex, int stableIndex) + protected override void OnInit() { - return ((long)(segmentIndex + 1) << 32) | (uint)stableIndex; + var tipRb = Rod.Asset.LineConnectorRigidbody; + anchorTransform = tipRb.transform; } } } \ No newline at end of file