using System; using System.Collections.Generic; using System.Text; using UnityEngine; namespace NBF { public enum LineType { Hand, HandDouble, Spinning, SpinningFloat, } public class FishingLineSolver : FGearBase { [SerializeField] public LineType LineType; [Header("References")] [SerializeField] private Transform anchorTransform; [SerializeField] private FishingLineNode[] logicalNodes = Array.Empty(); 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 /// /// 当前逻辑链总长度超出配置总长度的部分,小于等于零时记为 0。 /// public float CurrentStretchLength { get; private set; } /// /// 设置指定逻辑段的配置长度。 /// segmentIndex 为 0 时表示第一段;大于 0 时表示对应逻辑节点到下一个逻辑节点的线长。 /// public void SetLenght(float length, int segmentIndex = 0) { ConfigureStartNode(); CalculateLineRealLength(0f); } private void CalculateLineRealLength(float deltaTime) { } #endregion #region LineNode /// /// 当前配置的逻辑节点只读列表。 /// 外部可读取节点顺序,但不应直接修改数组内容。 /// public IReadOnlyList LogicalNodes => logicalNodes; /// /// 根据类型获取逻辑节点类型 /// /// /// 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]; } /// /// 获取当前起点逻辑节点。 /// 会返回配置顺序中第一个非空节点。 /// public FishingLineNode GetStartNode() { return FindFirstValidLogicalNode(); } /// /// 获取当前终点逻辑节点。 /// 会返回配置顺序中最后一个非空节点。 /// public FishingLineNode GetEndNode() { return FindLastValidLogicalNode(); } private FishingLineNode FindFirstValidLogicalNode() { if (logicalNodes == null) { return null; } for (var i = 0; i < logicalNodes.Length; i++) { if (logicalNodes[i] != null) { return logicalNodes[i]; } } return null; } private FishingLineNode FindLastValidLogicalNode() { if (logicalNodes == null) { return null; } for (var i = logicalNodes.Length - 1; i >= 0; i--) { if (logicalNodes[i] != null) { return logicalNodes[i]; } } return null; } #endregion #region 极限判定 [Header("Limit Detection")] [Min(0f)] // 极限判定的长度容差,允许链路在总长或单段长度上存在少量误差。 [SerializeField] private float lengthLimitTolerance = 0.01f; [Min(0f)] // 达到极限后,只有当前超长值大于该阈值时,才开始进入断线候选计时。 [SerializeField] private float breakStretchThreshold = 0.05f; /// /// 当鱼线达到断线条件时发出的一次性消息。 /// 外部可订阅该事件,在回调中执行切线、播放表现或状态切换。 /// public event Action OnLineBreakRequested; /// /// 当前断线候选状态的累计时间。 /// 只有在处于极限状态,且 CurrentStretchLength 大于断线阈值时才会累加;否则重置为 0。 /// public float LimitStateTime { get; private set; } /// /// 当前拉力极限百分比。 /// 当超长值小于等于 lengthLimitTolerance 时为 0; /// 当超长值大于等于 breakStretchThreshold 时为 100; /// 中间区间按线性比例映射,供 UI 显示使用。 /// 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; anchorTransform = tipRb.transform; } } }