using System.Collections.Generic;
using UnityEngine;
namespace NBF
{
///
/// 硬线系统测试脚本,直接读取 FLine 已配置的节点。
///
public class FLineTest : MonoBehaviour
{
[Header("测试控制")] [SerializeField] private KeyCode fixMiddleKey = KeyCode.M;
[SerializeField] private KeyCode applyForceKey = KeyCode.F;
[SerializeField] private Vector3 testForce = new Vector3(0f, 10f, 0f);
[Header("动态间距控制")]
// [SerializeField] private KeyCode extendKey = KeyCode.UpArrow;
// [SerializeField] private KeyCode contractKey = KeyCode.DownArrow;
[SerializeField]
private KeyCode resetKey = KeyCode.Alpha0;
[SerializeField] private KeyCode pullFirstKey = KeyCode.UpArrow;
[SerializeField] private KeyCode relaxFirstKey = KeyCode.DownArrow;
[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.OnLineBreakRequested += OnLineBreakRequested;
}
private void OnLineBreakRequested(FLine lineSolver)
{
Debug.LogError($"当前拉力达到极限,切线,极限时间={lineSolver.LimitStateTime}");
var nodes = line.GetLineNodes();
nodes[^1].Rigidbody.isKinematic = false;
// line.Body.isKinematic = false;
}
private void Update()
{
if (!EnsureCable())
return;
RefreshInitialLengths();
HandleInput();
if (line.CurrentBreakStretchPercent > 10f)
{
Debug.LogError(
$"当前极限情况,CurrentBreakStretchPercent={line.CurrentBreakStretchPercent} CurrentStretchLength={line.CurrentStretchLength} TotalLength={line.TotalLength} LimitStateTime={line.LimitStateTime}");
}
}
private bool EnsureCable()
{
if (line)
return true;
line = GetComponent();
return line != null;
}
private List GetNodes()
{
return line != null ? line.GetLineNodes() : null;
}
private void RefreshInitialLengths(bool force = false)
{
List nodes = GetNodes();
int segmentCount = nodes != null ? Mathf.Max(0, nodes.Count - 1) : 0;
if (!force && initialLengths != null && initialLengths.Length == segmentCount)
return;
initialLengths = new float[segmentCount];
for (int i = 0; i < segmentCount; i++)
{
float configuredLength = line.GetSegmentMaxLength(i);
if (configuredLength <= 0f)
configuredLength = Mathf.Max(0.01f, line.GetCurrentSegmentLength(i));
initialLengths[i] = Mathf.Max(0.01f, configuredLength);
}
}
private float GetCurrentMaxLength(int segmentIndex)
{
float length = line.GetSegmentMaxLength(segmentIndex);
return Mathf.Max(0.01f, length);
}
private float GetTargetMaxLength(int segmentIndex)
{
float length = line.GetTargetMaxLength(segmentIndex);
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 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;
middleNode.SetFixed(newState);
Debug.Log($"中间节点({middleIndex}) {(newState ? "固定" : "释放")} - 观察其他节点变化");
}
}
if (Input.GetKeyDown(applyForceKey) && nodes.Count > 2)
{
int randomIndex = Random.Range(1, nodes.Count - 1);
FLineLogicNode targetNode = nodes[randomIndex];
Rigidbody targetRb = targetNode ? targetNode.Rigidbody : null;
if (targetNode && targetRb && !targetRb.isKinematic)
{
targetNode.ApplyForce(testForce, ForceMode.Impulse);
Debug.Log($"对节点{randomIndex}施加力: {testForce} - 观察传递效果");
}
}
}
private void HandleDynamicDistanceControls()
{
if (line == null || initialLengths == null || initialLengths.Length == 0)
return;
// HandleLengthAdjustInput();
if (Input.GetKeyDown(resetKey))
ResetAllSegments();
if (Input.GetKeyDown(pullFirstKey))
PullFirstSegment(extendAmount * 0.5f);
if (Input.GetKeyDown(relaxFirstKey))
RelaxFirstSegment(extendAmount * 0.5f);
if (Input.mouseScrollDelta.y != 0f && Input.GetKey(KeyCode.LeftShift))
AdjustNearestSegment(Input.mouseScrollDelta.y * 0.1f);
}
private void ApplySegmentTargetLength(int segmentIndex, float targetLength)
{
line.SetSegmentMaxLength(segmentIndex, targetLength, transitionSpeed);
}
private void ResetAllSegments()
{
for (int i = 0; i < GetSegmentCount(); i++)
{
ApplySegmentTargetLength(i, initialLengths[i]);
}
Debug.Log("重置绳索到 FLine 初始节点配置");
}
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 AdjustNearestSegment(float amount)
{
int nearestSegment = FindNearestSegmentToCamera();
if (nearestSegment < 0 || nearestSegment >= GetSegmentCount())
return;
float targetLength = Mathf.Max(0.1f, GetTargetMaxLength(nearestSegment) + amount);
ApplySegmentTargetLength(nearestSegment, targetLength);
Debug.Log($"调整段{nearestSegment}到 {targetLength:F2}");
}
private int FindNearestSegmentToCamera()
{
Camera cam = Camera.main;
List nodes = GetNodes();
if (cam == null || nodes == null || nodes.Count < 2)
return -1;
int nearestSegment = -1;
float nearestDistance = float.MaxValue;
for (int i = 0; i < nodes.Count - 1; i++)
{
Rigidbody bodyA = nodes[i] ? nodes[i].Rigidbody : null;
Rigidbody bodyB = nodes[i + 1] ? nodes[i + 1].Rigidbody : null;
if (!bodyA || !bodyB)
continue;
Vector3 midPoint = (bodyA.position + bodyB.position) * 0.5f;
Vector3 screenPoint = cam.WorldToScreenPoint(midPoint);
Vector3 mousePos = Input.mousePosition;
float distance = Vector2.Distance(
new Vector2(screenPoint.x, screenPoint.y),
new Vector2(mousePos.x, mousePos.y)
);
if (distance < nearestDistance)
{
nearestDistance = distance;
nearestSegment = i;
}
}
return nearestSegment;
}
private void OnGUI()
{
if (!EnsureCable())
return;
RefreshInitialLengths();
List 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.Label($" {applyForceKey} - 随机施加力");
GUILayout.Space(10f);
GUILayout.Label("动态间距控制:");
GUILayout.Label($" {resetKey} - 重置到初始节点配置");
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();
}
}
}