The problem with the realistic rope was that it was difficult to make it less bouncy and the problem with the second rope was that it can't swing around itself because it consists of just one long spring. A solution to this problem is to throw out the springs and use Verlet integration instead of an Euler method.
To make this work you need two boxes. The rope will be attached to one of these boxes and the other box will simply hang from the rope. Then add the following script to a gameobject which has a line renderer attached to it:
using System.Collections; using System.Collections.Generic; using UnityEngine; //Simulate a rope with verlet integration and no springs public class RopeControllerRealisticNoSpring : MonoBehaviour { //Objects that will interact with the rope public Transform whatTheRopeIsConnectedTo; public Transform whatIsHangingFromTheRope; //Line renderer used to display the rope private LineRenderer lineRenderer; //A list with all rope section private List<RopeSection> allRopeSections = new List<RopeSection>(); //Rope data private float ropeSectionLength = 0.5f; private void Start() { //Init the line renderer we use to display the rope lineRenderer = GetComponent<LineRenderer>(); //Create the rope Vector3 ropeSectionPos = whatTheRopeIsConnectedTo.position; for (int i = 0; i < 15; i++) { allRopeSections.Add(new RopeSection(ropeSectionPos)); ropeSectionPos.y -= ropeSectionLength; } } private void Update() { //Display the rope with the line renderer DisplayRope(); //Move what is hanging from the rope to the end of the rope whatIsHangingFromTheRope.position = allRopeSections[allRopeSections.Count - 1].pos; //Make what's hanging from the rope look at the next to last rope position to make it rotate with the rope whatIsHangingFromTheRope.LookAt(allRopeSections[allRopeSections.Count - 2].pos); } private void FixedUpdate() { UpdateRopeSimulation(); } private void UpdateRopeSimulation() { Vector3 gravityVec = new Vector3(0f, -9.81f, 0f); float t = Time.fixedDeltaTime; //Move the first section to what the rope is hanging from RopeSection firstRopeSection = allRopeSections[0]; firstRopeSection.pos = whatTheRopeIsConnectedTo.position; allRopeSections[0] = firstRopeSection; //Move the other rope sections with Verlet integration for (int i = 1; i < allRopeSections.Count; i++) { RopeSection currentRopeSection = allRopeSections[i]; //Calculate velocity this update Vector3 vel = currentRopeSection.pos - currentRopeSection.oldPos; //Update the old position with the current position currentRopeSection.oldPos = currentRopeSection.pos; //Find the new position currentRopeSection.pos += vel; //Add gravity currentRopeSection.pos += gravityVec * t; //Add it back to the array allRopeSections[i] = currentRopeSection; } //Make sure the rope sections have the correct lengths for (int i = 0; i < 20; i++) { ImplementMaximumStretch(); } } //Make sure the rope sections have the correct lengths private void ImplementMaximumStretch() { for (int i = 0; i < allRopeSections.Count - 1; i++) { RopeSection topSection = allRopeSections[i]; RopeSection bottomSection = allRopeSections[i + 1]; //The distance between the sections float dist = (topSection.pos - bottomSection.pos).magnitude; //What's the stretch/compression float distError = Mathf.Abs(dist - ropeSectionLength); Vector3 changeDir = Vector3.zero; //Compress this sections if (dist > ropeSectionLength) { changeDir = (topSection.pos - bottomSection.pos).normalized; } //Extend this section else if (dist < ropeSectionLength) { changeDir = (bottomSection.pos - topSection.pos).normalized; } //Do nothing else { continue; } Vector3 change = changeDir * distError; if (i != 0) { bottomSection.pos += change * 0.5f; allRopeSections[i + 1] = bottomSection; topSection.pos -= change * 0.5f; allRopeSections[i] = topSection; } //Because the rope is connected to something else { bottomSection.pos += change; allRopeSections[i + 1] = bottomSection; } } } //Display the rope with a line renderer private void DisplayRope() { float ropeWidth = 0.2f; lineRenderer.startWidth = ropeWidth; lineRenderer.endWidth = ropeWidth; //An array with all rope section positions Vector3[] positions = new Vector3[allRopeSections.Count]; for (int i = 0; i < allRopeSections.Count; i++) { positions[i] = allRopeSections[i].pos; } lineRenderer.numPositions = positions.Length; lineRenderer.SetPositions(positions); } //A struct that will hold information about each rope section public struct RopeSection { public Vector3 pos; public Vector3 oldPos; //To write RopeSection.zero public static readonly RopeSection zero = new RopeSection(Vector3.zero); public RopeSection(Vector3 pos) { this.pos = pos; this.oldPos = pos; } } }
If you now move the box the rope is attached to then the rope should swing back and forth.