线逻辑处理
This commit is contained in:
@@ -144,7 +144,7 @@ LineRenderer:
|
||||
m_MaskInteraction: 0
|
||||
m_Positions:
|
||||
- {x: 0, y: 0, z: 0}
|
||||
- {x: 0, y: 0, z: 1}
|
||||
- {x: 0, y: 0, z: 0}
|
||||
m_Parameters:
|
||||
serializedVersion: 3
|
||||
widthMultiplier: 1
|
||||
@@ -395,7 +395,17 @@ MonoBehaviour:
|
||||
- {fileID: 3463242999848273700}
|
||||
- {fileID: 8491405271793597799}
|
||||
- {fileID: 2305106969988397276}
|
||||
positionCorrectionForce: 100
|
||||
dampingCoefficient: 10
|
||||
constraintIterations: 10
|
||||
useMassWeighting: 1
|
||||
showDebugInfo: 1
|
||||
defaultTransitionSpeed: 2
|
||||
LinelenghtDiferent: 0
|
||||
lengthLimitTolerance: 0.01
|
||||
breakStretchThreshold: 0.3
|
||||
breakStretchPercentMinThreshold: 0.06
|
||||
breakLimitDuration: 3
|
||||
--- !u!1 &1858052053854210
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -640,7 +650,7 @@ LineRenderer:
|
||||
m_MaskInteraction: 0
|
||||
m_Positions:
|
||||
- {x: 0, y: 0, z: 0}
|
||||
- {x: 0, y: 0, z: 1}
|
||||
- {x: 0, y: 0, z: 0}
|
||||
m_Parameters:
|
||||
serializedVersion: 3
|
||||
widthMultiplier: 1
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,15 @@ namespace NBF
|
||||
[Header("连接点配置")] [SerializeField] private Transform anchorTransform;
|
||||
[SerializeField] private List<FLineLogicNode> lineNodes = new List<FLineLogicNode>();
|
||||
|
||||
|
||||
[Header("物理参数")] [SerializeField] private float positionCorrectionForce = 100f;
|
||||
[SerializeField] private float dampingCoefficient = 10f;
|
||||
[SerializeField] private int constraintIterations = 10;
|
||||
[SerializeField] private bool useMassWeighting = true;
|
||||
[SerializeField] private bool showDebugInfo = true;
|
||||
[Header("动态间距设置")] [SerializeField] private float defaultTransitionSpeed = 2f; // 默认长度变化速度(单位/秒)
|
||||
|
||||
private LineMode _lineMode = LineMode.Joint;
|
||||
// [SerializeField] private bool isLureConnect;
|
||||
//
|
||||
// [SerializeField] private RodLine rodLine;
|
||||
@@ -53,34 +62,71 @@ namespace NBF
|
||||
//
|
||||
// public JointPinchController PinchController;
|
||||
|
||||
private readonly List<ConnectionConstraint> _constraints = new List<ConnectionConstraint>();
|
||||
|
||||
public FLineLogicNode StartNode { get; private set; }
|
||||
public FLineLogicNode BobberNode => GetNode(FLineLogicNodeType.Bobber);
|
||||
public FLineLogicNode EndNode { get; private set; }
|
||||
public LineMode LineMode => _lineMode;
|
||||
|
||||
|
||||
public float LinelenghtDiferent;
|
||||
|
||||
protected override void OnInit()
|
||||
|
||||
[System.Serializable]
|
||||
public class ConnectionConstraint
|
||||
{
|
||||
var tipRb = Rod.Asset.LineConnectorRigidbody;
|
||||
public FLineLogicNodeType NodeType;
|
||||
public Rigidbody bodyA;
|
||||
public Rigidbody bodyB;
|
||||
public float maxDistance;
|
||||
public float minDistance;
|
||||
public float currentDistance;
|
||||
public Vector3 direction;
|
||||
|
||||
anchorTransform = tipRb.transform;
|
||||
// StartCoroutine(LureUseGravity());
|
||||
// 动态目标距离(用于平滑过渡)
|
||||
public float targetMaxDistance;
|
||||
public bool hasPendingTransition;
|
||||
public bool hasPendingMaxTransition;
|
||||
public float maxTransitionSpeed;
|
||||
|
||||
// rodLine.GenerateLineRendererRope(guides.ToArray(), _LineThickness);
|
||||
public ConnectionConstraint(Rigidbody a, Rigidbody b, float maxDist, float minDist = 0f)
|
||||
{
|
||||
bodyA = a;
|
||||
bodyB = b;
|
||||
maxDistance = maxDist;
|
||||
minDistance = minDist;
|
||||
targetMaxDistance = maxDist;
|
||||
hasPendingTransition = false;
|
||||
hasPendingMaxTransition = false;
|
||||
maxTransitionSpeed = 0f;
|
||||
}
|
||||
|
||||
public void UpdateCurrentState()
|
||||
{
|
||||
if (bodyA && bodyB)
|
||||
{
|
||||
Vector3 delta = bodyB.position - bodyA.position;
|
||||
currentDistance = delta.magnitude;
|
||||
direction = currentDistance > 0.0001f ? delta.normalized : Vector3.right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
if (anchorTransform == null)
|
||||
{
|
||||
var tipRb = Rod.Asset.LineConnectorRigidbody;
|
||||
|
||||
anchorTransform = tipRb.transform;
|
||||
}
|
||||
}
|
||||
|
||||
// private IEnumerator LureUseGravity()
|
||||
// {
|
||||
// yield return 1;
|
||||
// EndNode.gameObject.SetActive(false);
|
||||
// EndNode.gameObject.SetActive(true);
|
||||
// yield return 1;
|
||||
// EndNode.Rigidbody.useGravity = true;
|
||||
// }
|
||||
|
||||
private void Start()
|
||||
{
|
||||
BuildConstraints();
|
||||
StartNode = GetNode(FLineLogicNodeType.Start);
|
||||
EndNode = GetNode(FLineLogicNodeType.End);
|
||||
}
|
||||
@@ -90,12 +136,33 @@ namespace NBF
|
||||
LinelenghtDiferent = GetLineDistance();
|
||||
|
||||
//非钓鱼状态
|
||||
Rod.PlayerItem.Tension = Mathf.Clamp(LinelenghtDiferent, 0f, 0.05f);
|
||||
if (Rod) Rod.PlayerItem.Tension = Mathf.Clamp(LinelenghtDiferent, 0f, 0.05f);
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
UpdateAnchorNode();
|
||||
FixedUpdateConstraints();
|
||||
}
|
||||
|
||||
public void ChangeMode(LineMode mode)
|
||||
{
|
||||
_lineMode = mode;
|
||||
|
||||
foreach (var fLineLogicNode in lineNodes)
|
||||
{
|
||||
fLineLogicNode.ChangeMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
public List<FLineLogicNode> GetLineNodes()
|
||||
{
|
||||
return new List<FLineLogicNode>(lineNodes);
|
||||
}
|
||||
|
||||
public void Print()
|
||||
{
|
||||
// Log.Info($"当前线情况 TotalLength={TotalLength} CurrentStretchLength={CurrentStretchLength}");
|
||||
}
|
||||
|
||||
#region 连接点
|
||||
@@ -140,14 +207,366 @@ namespace NBF
|
||||
var node = GetNode(type);
|
||||
if (node != null)
|
||||
{
|
||||
node.SetLenght(lenght);
|
||||
if (_lineMode == LineMode.Joint)
|
||||
{
|
||||
node.SetLenght(lenght);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSegmentMaxLength(lenght, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 脚本约束
|
||||
|
||||
private void FixedUpdateConstraints()
|
||||
{
|
||||
if (_lineMode != LineMode.Constraint) return;
|
||||
if (!enabled || lineNodes.Count < 2) return;
|
||||
|
||||
// 更新动态过渡
|
||||
UpdateTransitions();
|
||||
|
||||
for (int iteration = 0; iteration < constraintIterations; iteration++)
|
||||
{
|
||||
ApplyDistanceConstraints();
|
||||
}
|
||||
|
||||
ApplyDamping();
|
||||
}
|
||||
|
||||
private void BuildConstraints()
|
||||
{
|
||||
_constraints.Clear();
|
||||
if (lineNodes.Count < 2) return;
|
||||
|
||||
// 创建约束
|
||||
for (int i = 0; i < lineNodes.Count - 1; i++)
|
||||
{
|
||||
FLineLogicNode currentNode = lineNodes[i];
|
||||
FLineLogicNode nextNode = lineNodes[i + 1];
|
||||
Rigidbody bodyA = currentNode ? currentNode.Rigidbody : null;
|
||||
Rigidbody bodyB = nextNode ? nextNode.Rigidbody : null;
|
||||
|
||||
if (bodyA != null && bodyB != null)
|
||||
{
|
||||
var constraint = new ConnectionConstraint(
|
||||
bodyA,
|
||||
bodyB,
|
||||
nextNode.Lenght
|
||||
);
|
||||
constraint.NodeType = nextNode.NodeType;
|
||||
_constraints.Add(constraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyDistanceConstraints()
|
||||
{
|
||||
for (int i = 0; i < _constraints.Count; i++)
|
||||
{
|
||||
var constraint = _constraints[i];
|
||||
if (!constraint.bodyA || !constraint.bodyB) continue;
|
||||
|
||||
constraint.UpdateCurrentState();
|
||||
|
||||
float currentDist = constraint.currentDistance;
|
||||
float maxDist = constraint.maxDistance;
|
||||
float minDist = constraint.minDistance;
|
||||
|
||||
float error = 0f;
|
||||
bool needCorrection = false;
|
||||
|
||||
if (currentDist > maxDist)
|
||||
{
|
||||
error = currentDist - maxDist;
|
||||
needCorrection = true;
|
||||
}
|
||||
|
||||
if (!needCorrection || Mathf.Abs(error) < 0.0001f) continue;
|
||||
|
||||
float invMassA = constraint.bodyA.isKinematic ? 0f : 1f / constraint.bodyA.mass;
|
||||
float invMassB = constraint.bodyB.isKinematic ? 0f : 1f / constraint.bodyB.mass;
|
||||
float totalInvMass = invMassA + invMassB;
|
||||
|
||||
if (totalInvMass < 0.0001f) continue;
|
||||
|
||||
float weightA = useMassWeighting ? (invMassA / totalInvMass) : 0.5f;
|
||||
float weightB = useMassWeighting ? (invMassB / totalInvMass) : 0.5f;
|
||||
|
||||
Vector3 correction = constraint.direction * error;
|
||||
Vector3 positionCorrectionA = correction * weightA;
|
||||
Vector3 positionCorrectionB = -correction * weightB;
|
||||
|
||||
constraint.bodyA.position += positionCorrectionA;
|
||||
constraint.bodyB.position += positionCorrectionB;
|
||||
|
||||
Vector3 velocityCorrectionA = positionCorrectionA / Time.fixedDeltaTime;
|
||||
Vector3 velocityCorrectionB = positionCorrectionB / Time.fixedDeltaTime;
|
||||
|
||||
constraint.bodyA.AddForce(velocityCorrectionA * constraint.bodyA.mass, ForceMode.Impulse);
|
||||
constraint.bodyB.AddForce(velocityCorrectionB * constraint.bodyB.mass, ForceMode.Impulse);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyDamping()
|
||||
{
|
||||
for (int i = 0; i < _constraints.Count; i++)
|
||||
{
|
||||
var constraint = _constraints[i];
|
||||
if (!constraint.bodyA || !constraint.bodyB) continue;
|
||||
|
||||
if (constraint.currentDistance <= constraint.maxDistance) continue;
|
||||
|
||||
Vector3 relativeVelocity = constraint.bodyB.linearVelocity - constraint.bodyA.linearVelocity;
|
||||
float velocityInConstraintDir = Vector3.Dot(relativeVelocity, constraint.direction);
|
||||
|
||||
if (velocityInConstraintDir > 0)
|
||||
{
|
||||
float dampingForce = -velocityInConstraintDir * dampingCoefficient;
|
||||
Vector3 dampingImpulse = constraint.direction * dampingForce * Time.fixedDeltaTime;
|
||||
|
||||
constraint.bodyA.AddForce(-dampingImpulse * constraint.bodyA.mass, ForceMode.Impulse);
|
||||
constraint.bodyB.AddForce(dampingImpulse * constraint.bodyB.mass, ForceMode.Impulse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按速度过渡某段的最大距离
|
||||
/// </summary>
|
||||
private void SetSegmentMaxLength(float targetLength, FLineLogicNodeType type = FLineLogicNodeType.Bobber,
|
||||
float transitionSpeed = 2f)
|
||||
{
|
||||
var constraint = _constraints.Find(t => t.NodeType == type);
|
||||
if (constraint == null) return;
|
||||
|
||||
targetLength = Mathf.Max(0.01f, targetLength);
|
||||
float speed = Mathf.Max(0.01f, transitionSpeed > 0 ? transitionSpeed : defaultTransitionSpeed);
|
||||
|
||||
constraint.targetMaxDistance = targetLength;
|
||||
constraint.maxTransitionSpeed = speed;
|
||||
constraint.hasPendingMaxTransition = Mathf.Abs(constraint.maxDistance - targetLength) >= 0.0001f;
|
||||
constraint.hasPendingTransition = constraint.hasPendingMaxTransition;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 更新所有活跃的过渡
|
||||
/// </summary>
|
||||
private void UpdateTransitions()
|
||||
{
|
||||
float deltaTime = Time.fixedDeltaTime;
|
||||
|
||||
for (int i = 0; i < _constraints.Count; i++)
|
||||
{
|
||||
var constraint = _constraints[i];
|
||||
|
||||
if (constraint.hasPendingMaxTransition)
|
||||
{
|
||||
float nextMaxDistance = Mathf.MoveTowards(
|
||||
constraint.maxDistance,
|
||||
constraint.targetMaxDistance,
|
||||
constraint.maxTransitionSpeed * deltaTime
|
||||
);
|
||||
constraint.maxDistance = nextMaxDistance;
|
||||
var node = GetNode(constraint.NodeType);
|
||||
// SyncSegmentMaxLength(i, nextMaxDistance);
|
||||
node.SetLenght(nextMaxDistance);
|
||||
|
||||
if (Mathf.Abs(nextMaxDistance - constraint.targetMaxDistance) < 0.0001f)
|
||||
{
|
||||
constraint.maxDistance = constraint.targetMaxDistance;
|
||||
constraint.hasPendingMaxTransition = false;
|
||||
// SyncSegmentMaxLength(i, constraint.targetMaxDistance);
|
||||
node.SetLenght(constraint.targetMaxDistance);
|
||||
}
|
||||
}
|
||||
|
||||
constraint.hasPendingTransition = constraint.hasPendingMaxTransition;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取某个段是否正在进行过渡
|
||||
/// </summary>
|
||||
public bool IsSegmentTransitioning(int segmentIndex)
|
||||
{
|
||||
if (segmentIndex >= 0 && segmentIndex < _constraints.Count)
|
||||
{
|
||||
return _constraints[segmentIndex].hasPendingTransition;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// #region 极限判定
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 当前逻辑链总长度超出配置总长度的部分,小于等于零时记为 0。
|
||||
// /// </summary>
|
||||
// [Header("Limit Detection")]
|
||||
// public float CurrentStretchLength { get; private set; }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 总长度
|
||||
// /// </summary>
|
||||
// public float TotalLength { get; private set; }
|
||||
//
|
||||
// [Min(0f)]
|
||||
// // 极限判定的长度容差,允许链路在总长或单段长度上存在少量误差。
|
||||
// [SerializeField]
|
||||
// private float lengthLimitTolerance = 0.01f;
|
||||
//
|
||||
// [Min(0f)]
|
||||
// // 达到极限后,只有当前超长值大于该阈值时,才开始进入断线候选计时。
|
||||
// [SerializeField]
|
||||
// private float breakStretchThreshold = 0.3f;
|
||||
//
|
||||
// [Min(0f)]
|
||||
// // UI 百分比开始起算的最小超长值;低于或等于该值时统一按 0% 处理。
|
||||
// [SerializeField]
|
||||
// private float breakStretchPercentMinThreshold = 0.06f;
|
||||
//
|
||||
// [Min(0f)]
|
||||
// // 断线候选状态允许持续的最大时间;超过后会发出一次断线消息。
|
||||
// [SerializeField]
|
||||
// private float breakLimitDuration = 3f;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 当鱼线达到断线条件时发出的一次性消息。
|
||||
// /// 外部可订阅该事件,在回调中执行切线、播放表现或状态切换。
|
||||
// /// </summary>
|
||||
// public event Action<FLine> OnLineBreakRequested;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 当前是否处于极限状态。
|
||||
// /// 只要整链超出总长度容差,或任一逻辑段超出单段容差,即认为到达极限。
|
||||
// /// </summary>
|
||||
// public bool IsAtLimit { get; private set; }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 当前断线候选状态的累计时间。
|
||||
// /// 只有在处于极限状态,且 CurrentStretchLength 大于断线阈值时才会累加;否则重置为 0。
|
||||
// /// </summary>
|
||||
// public float LimitStateTime { get; private set; }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 当前极限断线消息是否已经发出过。
|
||||
// /// 在退出断线候选状态前只会发一次,避免重复通知。
|
||||
// /// </summary>
|
||||
// public bool HasBreakNotificationSent { get; private set; }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 当前拉力极限百分比。
|
||||
// /// 当超长值小于等于 breakStretchPercentMinThreshold 时为 0;
|
||||
// /// 当超长值大于等于 breakStretchThreshold 时为 100;
|
||||
// /// 中间区间按线性比例映射,供 UI 显示使用。
|
||||
// /// </summary>
|
||||
// public float CurrentBreakStretchPercent => EvaluateBreakStretchPercent(CurrentStretchLength);
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 当前是否正在进行断线候选计时。
|
||||
// /// </summary>
|
||||
// public bool IsBreakCountdownActive => IsAtLimit && CurrentStretchLength > breakStretchThreshold;
|
||||
//
|
||||
// private float EvaluateBreakStretchPercent(float stretchLength)
|
||||
// {
|
||||
// var percentMinThreshold = Mathf.Max(lengthLimitTolerance, breakStretchPercentMinThreshold);
|
||||
//
|
||||
// if (stretchLength <= percentMinThreshold)
|
||||
// {
|
||||
// return 0f;
|
||||
// }
|
||||
//
|
||||
// if (stretchLength >= breakStretchThreshold)
|
||||
// {
|
||||
// return 100f;
|
||||
// }
|
||||
//
|
||||
// if (breakStretchThreshold <= percentMinThreshold)
|
||||
// {
|
||||
// return 100f;
|
||||
// }
|
||||
//
|
||||
// return Mathf.InverseLerp(percentMinThreshold, breakStretchThreshold, stretchLength) * 100f;
|
||||
// }
|
||||
//
|
||||
// private void SetLimitState(bool isAtLimit)
|
||||
// {
|
||||
// IsAtLimit = isAtLimit;
|
||||
// }
|
||||
//
|
||||
// private void UpdateBreakCountdown(float deltaTime)
|
||||
// {
|
||||
// if (lineNodes.Count < 2)
|
||||
// {
|
||||
// SetLimitState(false);
|
||||
// ResetLimitState();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// var startNode = lineNodes[0];
|
||||
// var endNode = lineNodes[^1];
|
||||
// TotalLength = 0;
|
||||
// foreach (var node in lineNodes)
|
||||
// {
|
||||
// TotalLength += node.Lenght;
|
||||
// }
|
||||
//
|
||||
// var realLen = Vector3.Distance(startNode.transform.position, endNode.transform.position);
|
||||
// CurrentStretchLength = realLen - TotalLength;
|
||||
// if (CurrentStretchLength < 0f)
|
||||
// {
|
||||
// CurrentStretchLength = 0f;
|
||||
// }
|
||||
//
|
||||
// SetLimitState(CurrentStretchLength > lengthLimitTolerance);
|
||||
// if (LineMode != LineMode.Constraint) return;
|
||||
//
|
||||
// if (!IsBreakCountdownActive)
|
||||
// {
|
||||
// LimitStateTime = 0f;
|
||||
// HasBreakNotificationSent = false;
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// LimitStateTime += Mathf.Max(0f, deltaTime);
|
||||
// if (HasBreakNotificationSent || LimitStateTime < breakLimitDuration)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// HasBreakNotificationSent = true;
|
||||
// NotifyLineBreakRequested();
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 发出鱼线达到断线条件的消息。
|
||||
// /// 这里预留给外部订阅,当前不在求解器内部直接执行断线逻辑。
|
||||
// /// </summary>
|
||||
// private void NotifyLineBreakRequested()
|
||||
// {
|
||||
// OnLineBreakRequested?.Invoke(this);
|
||||
// }
|
||||
//
|
||||
// private void ResetLimitState()
|
||||
// {
|
||||
// CurrentStretchLength = 0f;
|
||||
// IsAtLimit = false;
|
||||
// LimitStateTime = 0f;
|
||||
// HasBreakNotificationSent = false;
|
||||
// }
|
||||
//
|
||||
// #endregion
|
||||
|
||||
#region Tension
|
||||
|
||||
private float GetLineDistance()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
@@ -23,6 +24,7 @@ namespace NBF
|
||||
|
||||
private float _lenght;
|
||||
|
||||
public Rigidbody PreRigidbody => preRigidbody;
|
||||
public Rigidbody Rigidbody => _rb;
|
||||
public FLine ParentCable => _parentCable;
|
||||
public SpringJoint Joint => _joint;
|
||||
@@ -60,11 +62,40 @@ namespace NBF
|
||||
{
|
||||
if (mode == LineMode.Joint)
|
||||
{
|
||||
if (_joint) _joint.connectedBody = preRigidbody;
|
||||
if (_joint)
|
||||
{
|
||||
StartCoroutine(ReconnectedBody());
|
||||
}
|
||||
}
|
||||
else if (mode == LineMode.Constraint)
|
||||
{
|
||||
if (_joint) _joint.connectedBody = null;
|
||||
if (NodeType == FLineLogicNodeType.End)
|
||||
{
|
||||
Rigidbody.isKinematic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ReconnectedBody()
|
||||
{
|
||||
_joint.connectedBody = preRigidbody;
|
||||
yield return 1;
|
||||
Rigidbody.position = preRigidbody.position;
|
||||
preRigidbody.isKinematic = true;
|
||||
preRigidbody.linearVelocity = Vector3.zero;
|
||||
preRigidbody.angularVelocity = Vector3.zero;
|
||||
if (NodeType != FLineLogicNodeType.Start)
|
||||
{
|
||||
preRigidbody.isKinematic = false;
|
||||
}
|
||||
|
||||
yield return 1;
|
||||
preRigidbody.linearVelocity = Vector3.zero;
|
||||
preRigidbody.angularVelocity = Vector3.zero;
|
||||
if (NodeType == FLineLogicNodeType.End)
|
||||
{
|
||||
Rigidbody.isKinematic = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
268
Assets/Scripts/Fishing/New/View/Player/FishingLine/FLineTest.cs
Normal file
268
Assets/Scripts/Fishing/New/View/Player/FishingLine/FLineTest.cs
Normal file
@@ -0,0 +1,268 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
/// <summary>
|
||||
/// 硬线系统测试脚本,直接读取 FLine 已配置的节点。
|
||||
/// </summary>
|
||||
public class FLineTest : MonoBehaviour
|
||||
{
|
||||
[Header("测试控制")] [SerializeField] private KeyCode fixMiddleKey = KeyCode.M;
|
||||
|
||||
[Header("动态间距控制")] [SerializeField] private KeyCode pullFirstKey = KeyCode.UpArrow;
|
||||
[SerializeField] private KeyCode relaxFirstKey = KeyCode.DownArrow;
|
||||
[SerializeField] private KeyCode fixedKey = KeyCode.F;
|
||||
[SerializeField] private KeyCode debugKey = KeyCode.D;
|
||||
[SerializeField, Min(0.01f)] private float extendAmount = 0.5f;
|
||||
[SerializeField, Min(0.01f)] private float holdAdjustSpeed = 1f;
|
||||
[SerializeField, Min(0.01f)] private float transitionSpeed = 2f;
|
||||
[SerializeField] private bool smoothTransition = true;
|
||||
|
||||
[SerializeField] private FLine line;
|
||||
private float[] initialLengths;
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
extendAmount = Mathf.Max(0.01f, extendAmount);
|
||||
holdAdjustSpeed = Mathf.Max(0.01f, holdAdjustSpeed);
|
||||
transitionSpeed = Mathf.Max(0.01f, transitionSpeed);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
RefreshInitialLengths(true);
|
||||
line.SetLenght(0.2f);
|
||||
line.SetLenght(0.2f, FLineLogicNodeType.End);
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!EnsureCable())
|
||||
return;
|
||||
|
||||
RefreshInitialLengths();
|
||||
HandleInput();
|
||||
|
||||
// if (line.CurrentBreakStretchPercent > 10f)
|
||||
// {
|
||||
// if (line.LineMode == LineMode.Constraint)
|
||||
// Debug.LogError(
|
||||
// $"当前极限情况,CurrentBreakStretchPercent={line.CurrentBreakStretchPercent} CurrentStretchLength={line.CurrentStretchLength} TotalLength={line.TotalLength} LimitStateTime={line.LimitStateTime}");
|
||||
// }
|
||||
}
|
||||
|
||||
private bool EnsureCable()
|
||||
{
|
||||
if (line)
|
||||
return true;
|
||||
|
||||
line = GetComponent<FLine>();
|
||||
return line != null;
|
||||
}
|
||||
|
||||
private List<FLineLogicNode> GetNodes()
|
||||
{
|
||||
return line != null ? line.GetLineNodes() : null;
|
||||
}
|
||||
|
||||
private void RefreshInitialLengths(bool force = false)
|
||||
{
|
||||
List<FLineLogicNode> nodes = GetNodes();
|
||||
int segmentCount = nodes != null ? Mathf.Max(0, nodes.Count - 1) : 0;
|
||||
|
||||
if (!force && initialLengths != null && initialLengths.Length == segmentCount)
|
||||
return;
|
||||
|
||||
initialLengths = new float[nodes.Count];
|
||||
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
var node = nodes[i];
|
||||
initialLengths[i] = Mathf.Max(0.01f, node.Lenght);
|
||||
}
|
||||
}
|
||||
|
||||
private float GetCurrentMaxLength(int segmentIndex)
|
||||
{
|
||||
var nodes = line.GetLineNodes();
|
||||
var node = nodes[segmentIndex];
|
||||
// float length = line.GetSegmentMaxLength(segmentIndex);
|
||||
return Mathf.Max(0.01f, node.Lenght);
|
||||
}
|
||||
|
||||
private float GetTargetMaxLength(int segmentIndex)
|
||||
{
|
||||
var nodes = line.GetLineNodes();
|
||||
var node = nodes[segmentIndex];
|
||||
float length = node.Lenght;
|
||||
// if (length <= 0f)
|
||||
// length = line.GetSegmentMaxLength(segmentIndex);
|
||||
|
||||
return Mathf.Max(0.01f, length);
|
||||
}
|
||||
|
||||
private int GetSegmentCount()
|
||||
{
|
||||
return initialLengths != null ? initialLengths.Length : 0;
|
||||
}
|
||||
|
||||
private void HandleInput()
|
||||
{
|
||||
HandleOriginalControls();
|
||||
HandleDynamicDistanceControls();
|
||||
}
|
||||
|
||||
private void HandleOriginalControls()
|
||||
{
|
||||
List<FLineLogicNode> nodes = GetNodes();
|
||||
if (nodes == null)
|
||||
return;
|
||||
|
||||
if (Input.GetKeyDown(fixMiddleKey) && nodes.Count >= 3)
|
||||
{
|
||||
int middleIndex = nodes.Count / 2;
|
||||
FLineLogicNode middleNode = nodes[middleIndex];
|
||||
Rigidbody middleRb = middleNode ? middleNode.Rigidbody : null;
|
||||
|
||||
if (middleNode && middleRb)
|
||||
{
|
||||
bool newState = !middleRb.isKinematic;
|
||||
Debug.Log($"中间节点({middleIndex}) {(newState ? "固定" : "释放")} - 观察其他节点变化");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDynamicDistanceControls()
|
||||
{
|
||||
if (line == null || initialLengths == null || initialLengths.Length == 0)
|
||||
return;
|
||||
|
||||
if (Input.GetKeyDown(pullFirstKey))
|
||||
PullFirstSegment(extendAmount * 0.5f);
|
||||
|
||||
if (Input.GetKeyDown(relaxFirstKey))
|
||||
RelaxFirstSegment(extendAmount * 0.5f);
|
||||
|
||||
if (Input.GetKeyDown(fixedKey))
|
||||
{
|
||||
if (line.LineMode == LineMode.Joint) line.ChangeMode(LineMode.Constraint);
|
||||
else line.ChangeMode(LineMode.Joint);
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(debugKey))
|
||||
{
|
||||
line.Print();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplySegmentTargetLength(FLineLogicNodeType type, float targetLength)
|
||||
{
|
||||
line.SetLenght(targetLength, type);
|
||||
}
|
||||
|
||||
private void PullFirstSegment(float amount)
|
||||
{
|
||||
if (GetSegmentCount() <= 0)
|
||||
return;
|
||||
|
||||
float targetLength = Mathf.Max(0.1f, GetTargetMaxLength(0) - amount);
|
||||
ApplySegmentTargetLength(0, targetLength);
|
||||
Debug.Log($"拉紧第一段到 {targetLength:F2}");
|
||||
}
|
||||
|
||||
private void RelaxFirstSegment(float amount)
|
||||
{
|
||||
if (GetSegmentCount() <= 0)
|
||||
return;
|
||||
|
||||
float targetLength = GetTargetMaxLength(0) + amount;
|
||||
ApplySegmentTargetLength(0, targetLength);
|
||||
Debug.Log($"放松第一段到 {targetLength:F2}");
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (!EnsureCable())
|
||||
return;
|
||||
|
||||
RefreshInitialLengths();
|
||||
|
||||
List<FLineLogicNode> nodes = GetNodes();
|
||||
int nodeCount = nodes != null ? nodes.Count : 0;
|
||||
|
||||
GUILayout.BeginArea(new Rect(10f, 10f, 360f, 260f));
|
||||
GUILayout.Label("=== 硬线系统测试控制 ===");
|
||||
GUILayout.Label("原始控制:");
|
||||
GUILayout.Label($" {fixMiddleKey} - 固定/释放中间节点");
|
||||
GUILayout.Space(10f);
|
||||
GUILayout.Label("动态间距控制:");
|
||||
GUILayout.Label($" {pullFirstKey} - 拉紧第一段");
|
||||
GUILayout.Label($" {relaxFirstKey} - 放松第一段");
|
||||
GUILayout.Label(" Shift+滚轮 - 调整最近段");
|
||||
GUILayout.Space(10f);
|
||||
GUILayout.Label("设置:");
|
||||
GUILayout.Label($" 节点数: {nodeCount}");
|
||||
GUILayout.Label(" 初始长度来源: FLine 节点配置");
|
||||
GUILayout.Label($" 过渡模式: {(smoothTransition ? "平滑" : "即时")}");
|
||||
if (smoothTransition)
|
||||
GUILayout.Label($" 过渡速度: {transitionSpeed:F1}");
|
||||
|
||||
GUILayout.EndArea();
|
||||
|
||||
GUILayout.BeginArea(new Rect(10f, 280f, 360f, 220f));
|
||||
GUILayout.Label("=== 各段实际长度 ===");
|
||||
|
||||
// for (int i = 0; i < Mathf.Min(GetSegmentCount(), 10); i++)
|
||||
// {
|
||||
// Rigidbody bodyA = nodes[i] ? nodes[i].Rigidbody : null;
|
||||
// Rigidbody bodyB = nodes[i + 1] ? nodes[i + 1].Rigidbody : null;
|
||||
// if (!bodyA || !bodyB)
|
||||
// continue;
|
||||
//
|
||||
// float actualDistance = Vector3.Distance(bodyA.position, bodyB.position);
|
||||
// float currentLimit = GetCurrentMaxLength(i);
|
||||
// float targetLimit = GetTargetMaxLength(i);
|
||||
//
|
||||
// string segmentInfo = $"段{i}: {actualDistance:F2} (限制: {currentLimit:F2}";
|
||||
// if (line.IsSegmentTransitioning(i))
|
||||
// segmentInfo += $" -> {targetLimit:F2}";
|
||||
//
|
||||
// segmentInfo += ")";
|
||||
//
|
||||
// if (actualDistance > targetLimit * 1.1f)
|
||||
// {
|
||||
// GUI.color = Color.red;
|
||||
// }
|
||||
// else if (line.IsSegmentTransitioning(i))
|
||||
// {
|
||||
// GUI.color = Color.yellow;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// GUI.color = Color.green;
|
||||
// }
|
||||
//
|
||||
// GUILayout.Label(segmentInfo);
|
||||
// }
|
||||
|
||||
GUI.color = Color.white;
|
||||
|
||||
bool anyTransitioning = false;
|
||||
for (int i = 0; i < GetSegmentCount(); i++)
|
||||
{
|
||||
if (line.IsSegmentTransitioning(i))
|
||||
{
|
||||
anyTransitioning = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyTransitioning)
|
||||
GUILayout.Label("状态: 过渡中...");
|
||||
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 535734a3b6eb4e16847b2fc9d9dee1c3
|
||||
timeCreated: 1777263325
|
||||
@@ -15,35 +15,35 @@ EditorUserSettings:
|
||||
value: 2550581500
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-0:
|
||||
value: 55540305570d0f0e0c5e5e2115710d44174e4e2b7b7e77662f2d1c61b5b06069
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-1:
|
||||
value: 5452500303515f0a5f5b5a7445775e46401519787c717f677d784860e3b1676c
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-2:
|
||||
value: 050402550007590a0f565f2714200c44144e492f2f70753175711f66e0b8303c
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-3:
|
||||
RecentlyUsedSceneGuid-1:
|
||||
value: 06070c5f5c075c5e5e085476427a0a44474e1c2f7f7a73362f2d4d36b5b1633d
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-4:
|
||||
RecentlyUsedSceneGuid-2:
|
||||
value: 0005505f515750595e5f5f23412507441216497f2d7f24367e711c64b6b86c61
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-5:
|
||||
RecentlyUsedSceneGuid-3:
|
||||
value: 54070c5452075002590c0871127b5a4443161c2f797176312c2f1e6bb1b4353d
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-6:
|
||||
RecentlyUsedSceneGuid-4:
|
||||
value: 5309035757065a0a54575f7216265c4444151d28792e72627d2f1935bbb8673a
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-7:
|
||||
RecentlyUsedSceneGuid-5:
|
||||
value: 00050c5150005f5f54560f2640270d4410161c28282b72357e7c4835e4b63760
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-8:
|
||||
RecentlyUsedSceneGuid-6:
|
||||
value: 06090c5f54015f5a0f085b7b11765d444e4e1e287429773178704561b3b23561
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-9:
|
||||
RecentlyUsedSceneGuid-7:
|
||||
value: 0257035f51050d090f0f5d734521094414164e797e7a20667d7a4536e0e36461
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-8:
|
||||
value: 5505015f5c515a085f5b092149760f441716407a787d7564287b1b36e7e1366e
|
||||
flags: 0
|
||||
RecentlyUsedSceneGuid-9:
|
||||
value: 07060c5454040c0a545b547240700a441216417e7f2e7268752c4966b4b0663d
|
||||
flags: 0
|
||||
UnityEditor.ShaderGraph.Blackboard:
|
||||
value: 18135939215a0a5004000b0e15254b524c030a3f2964643d120d1230e9e93a3fd6e826abbd2e2d293c4ead313b08042de6030a0afa240c0d020be94c4ba75e435d8715fa32c70d15d11612dacc11fee5d3c5d1fe9ab1bf968e93e2ffcbc3e7e2f0b3ffe0e8b0be9afeffa9ffff8e85dd8390e2969e8899daa7
|
||||
flags: 0
|
||||
|
||||
Reference in New Issue
Block a user