using UnityEngine; [RequireComponent(typeof(LineRenderer))] public class Rope : MonoBehaviour { [Header("Attachment Points")] [SerializeField] public Transform startAttachment; // 绳子起点绑定的Transform [SerializeField] public Transform endAttachment; // 绳子终点绑定的Transform [Header("Verlet Parameters")] [SerializeField] float nodeDistance = 0.35f; [SerializeField] float nodeColliderRadius = 0.2f; [SerializeField] float gravityStrength = 2; [SerializeField] float totalLength = 10f; [SerializeField, Range(0, 1)] float velocityDampen = 0.95f; [SerializeField, Range(0, 0.99f)] float stiffness = 0.8f; [SerializeField] int iterateCollisionsEvery = 1; [SerializeField] int iterations = 10; [SerializeField] int colliderBufferSize = 1; [Header("Line Renderer")] [SerializeField] float ropeWidth = 0.1f; // 私有变量 Vector3 gravity; // 数组和缓存 Vector3[] currentNodePositions; Vector3[] previousNodePositions; Collider[] colliderHitBuffer; LineRenderer lineRenderer; GameObject nodeTester; SphereCollider nodeCollider; int totalNodes; float lastTotalLength; void Awake() { // 获取组件引用 lineRenderer = GetComponent(); gravity = new Vector3(0, -gravityStrength, 0); // 初始化节点测试器 nodeTester = new GameObject("Node Tester"); nodeTester.layer = 8; nodeCollider = nodeTester.AddComponent(); nodeCollider.radius = nodeColliderRadius; // 初始化长度跟踪 lastTotalLength = totalLength; InitializeRope(); } void InitializeRope() { // 计算节点数量 totalNodes = Mathf.FloorToInt(totalLength / nodeDistance) + 1; float remainingLength = totalLength % nodeDistance; if (remainingLength > 0 && totalLength > nodeDistance) { totalNodes++; } // 初始化或调整数组大小 System.Array.Resize(ref currentNodePositions, totalNodes); System.Array.Resize(ref previousNodePositions, totalNodes); colliderHitBuffer = new Collider[colliderBufferSize]; // 初始化节点位置 Vector3 startPos = startAttachment != null ? startAttachment.position : transform.position; for (int i = 0; i < totalNodes; i++) { float distance = (i == totalNodes - 1 && remainingLength > 0) ? remainingLength : nodeDistance; currentNodePositions[i] = startPos; previousNodePositions[i] = startPos; startPos.y -= distance; } // 设置线渲染器 lineRenderer.startWidth = ropeWidth; lineRenderer.endWidth = ropeWidth; lineRenderer.positionCount = totalNodes; } void Update() { // 检查长度是否变化 if (!Mathf.Approximately(totalLength, lastTotalLength)) { AdjustRopeLength(); lastTotalLength = totalLength; } DrawRope(); } void AdjustRopeLength() { Vector3[] oldPositions = (Vector3[])currentNodePositions.Clone(); Vector3[] oldPrevPositions = (Vector3[])previousNodePositions.Clone(); InitializeRope(); int copyLength = Mathf.Min(oldPositions.Length, currentNodePositions.Length); System.Array.Copy(oldPositions, currentNodePositions, copyLength); System.Array.Copy(oldPrevPositions, previousNodePositions, copyLength); if (currentNodePositions.Length > oldPositions.Length) { Vector3 lastPos = oldPositions[oldPositions.Length - 1]; for (int i = oldPositions.Length; i < currentNodePositions.Length; i++) { float distance = (i == currentNodePositions.Length - 1 && (totalLength % nodeDistance) > 0) ? (totalLength % nodeDistance) : nodeDistance; lastPos.y -= distance; currentNodePositions[i] = lastPos; previousNodePositions[i] = lastPos; } } } void FixedUpdate() { Simulate(); for (int i = 0; i < iterations; i++) { ApplyConstraint(); if (i % (iterateCollisionsEvery + 1) == 0) { AdjustCollisions(); } } } void Simulate() { float fixedDt = Time.fixedDeltaTime; for (int i = 0; i < totalNodes; i++) { Vector3 velocity = (currentNodePositions[i] - previousNodePositions[i]) * velocityDampen; previousNodePositions[i] = currentNodePositions[i]; currentNodePositions[i] += velocity + gravity * fixedDt; } } void ApplyConstraint() { // 绑定到起点Transform if (startAttachment != null) { currentNodePositions[0] = startAttachment.position; } // 绑定到终点Transform if (endAttachment != null) { currentNodePositions[totalNodes - 1] = endAttachment.position; } float halfStiffness = 0.5f * stiffness; int nodeCountMinusOne = totalNodes - 1; for (int i = 0; i < nodeCountMinusOne; i++) { Vector3 node1 = currentNodePositions[i]; Vector3 node2 = currentNodePositions[i + 1]; Vector3 diff = node1 - node2; float desiredDistance = (i == nodeCountMinusOne - 1 && (totalLength % nodeDistance) > 0) ? (totalLength % nodeDistance) : nodeDistance; float sqrDesiredDistance = desiredDistance * desiredDistance; float sqrDistance = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z; if (Mathf.Abs(sqrDistance - sqrDesiredDistance) > 0.001f) { float distance = Mathf.Sqrt(sqrDistance); float difference = desiredDistance - distance; Vector3 direction = diff / distance; Vector3 adjustment = direction * (difference * halfStiffness); currentNodePositions[i] += adjustment; currentNodePositions[i + 1] -= adjustment; } } } void AdjustCollisions() { for (int i = 1; i < totalNodes; i += 2) { int hits = Physics.OverlapSphereNonAlloc( currentNodePositions[i], nodeColliderRadius, colliderHitBuffer, ~(1 << 8)); for (int n = 0; n < hits; n++) { if (Physics.ComputePenetration( nodeCollider, currentNodePositions[i], Quaternion.identity, colliderHitBuffer[n], colliderHitBuffer[n].transform.position, colliderHitBuffer[n].transform.rotation, out Vector3 direction, out float distance)) { currentNodePositions[i] += direction * distance; } } } } void DrawRope() { lineRenderer.positionCount = totalNodes; lineRenderer.SetPositions(currentNodePositions); } void OnDestroy() { if (nodeTester != null) { Destroy(nodeTester); } } // 公开方法用于动态设置绑定点 public void SetAttachments(Transform start, Transform end) { startAttachment = start; endAttachment = end; } }