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(); } } }