Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Swinging rope is loosing lots of energy

Discussion in 'Physics' started by jak6jak, Mar 12, 2020.

  1. jak6jak

    jak6jak

    Joined:
    Apr 17, 2013
    Posts:
    25
    This is a tough question. I tried implementing velocity verlet using the c++ implementation on this Wikipedia page: https://en.wikipedia.org/wiki/Verlet_integration I then added some constraints to create a rope. The problem is that the rope swings very slowly and doesn't even make a complete swinging motion. I understand energy loss is expected but this is way too much. It gets "stuck" at 90 degrees. Is the wiki implementation incorrect or am I to blame? If it's not the velocity verlet's fault I think it is the constraints. Unfortunately I don't know enough about physic simulation to fix it. Can someone help me?


    Code (CSharp):
    1.  using System.Collections;
    2.     using System.Collections.Generic;
    3.     using UnityEngine;
    4.  
    5.     public class VerletHookShot : MonoBehaviour
    6.     {
    7.  
    8.         public GameObject pivot; // point the rope is "hooked" on to
    9.         private Rigidbody pivotRb;
    10.         private LineRenderer lineRenderer;
    11.         private int ropeLength = 5; // number of segments
    12.         private List<RopeSegment> ropeSegments = new List<RopeSegment>();
    13.         private float ropeSegLen;
    14.  
    15.         private float lineWidth = 0.1f;
    16.  
    17.     void Start()
    18.     {
    19.         pivotRb = pivot.GetComponent<Rigidbody>();
    20.         this.lineRenderer = this.GetComponent<LineRenderer>();
    21.         Vector3 ropeStartPoint = pivotRb.transform.position;
    22.         Vector3 ropeEndPoint = Vector3.zero;
    23.  
    24.         ropeSegLen = Vector3.Distance(ropeStartPoint, ropeEndPoint) / (float)(ropeLength -1 );
    25.         Debug.Log(ropeSegLen);
    26.         // starts the rope in a diagonal position
    27.         for (int i = 0; i < ropeLength; i++)
    28.         {
    29.             float per = (float)i / ropeLength;
    30.             ropeStartPoint = Vector3.Lerp(ropeStartPoint, ropeEndPoint, per);
    31.             this.ropeSegments.Add(new RopeSegment(ropeStartPoint, Vector3.zero, Vector3.zero));
    32.         }
    33.      
    34.     }
    35.  
    36.     void Update()
    37.     {
    38.         this.DrawRope();
    39.     }
    40.     private void FixedUpdate()
    41.     {
    42.         this.Simulate();
    43.     }
    44.     private Vector3 apply_forces(RopeSegment segment)
    45.     {
    46.         Vector3 grav_acc = Physics.gravity;
    47.         //removing drag force does nothing
    48.         Vector3 drag_force = 0.5f * 0.1f * (segment.new_Vel * segment.new_Vel.magnitude);
    49.         Vector3 drag_acc = drag_force;
    50.         return grav_acc - drag_acc;
    51.     }
    52.  
    53.     private void Simulate()
    54.     {
    55.         Vector3 forceGravity = Physics.gravity;
    56.  
    57.         for (int i = 1; i < this.ropeLength; i++)
    58.         {
    59.             RopeSegment firstSegment = this.ropeSegments[i];
    60.             firstSegment.posNow = firstSegment.posOld + firstSegment.old_Vel * Time.deltaTime +  firstSegment.old_acc * (Mathf.Pow(Time.fixedDeltaTime, 2) * .05f);
    61.             firstSegment.new_acc = apply_forces(firstSegment);
    62.             firstSegment.new_Vel = firstSegment.old_Vel + (firstSegment.old_acc + firstSegment.new_acc) * Time.fixedDeltaTime * .05f;
    63.  
    64.             firstSegment.posOld = firstSegment.posNow;
    65.             firstSegment.old_Vel = firstSegment.new_Vel;
    66.             firstSegment.old_acc = firstSegment.new_acc;
    67.             this.ropeSegments[i] = firstSegment;
    68.  
    69.             // regular verlet code wasn't working either
    70.             /*Vector3 velocity = firstSegment.posNow - firstSegment.posOld;
    71.             firstSegment.posOld = firstSegment.posNow;
    72.             firstSegment.posNow += velocity;
    73.             firstSegment.posNow += forceGravity * Time.fixedDeltaTime;
    74.             this.ropeSegments[i] = firstSegment;*/
    75.         }
    76.  
    77.         //Constraints
    78.        for (int i = 0; i < 50; i++)
    79.        {
    80.             this.ApplyConstraint();
    81.        }
    82.     }
    83.     private void ApplyConstraint()
    84.     {
    85.         //constraint to first point
    86.         RopeSegment firstSegment = this.ropeSegments[0];
    87.         firstSegment.posNow = pivotRb.transform.position;
    88.         this.ropeSegments[0] = firstSegment;
    89.  
    90.         for (int i = 0; i < this.ropeLength - 1; i++)
    91.         {
    92.             RopeSegment firstSeg = this.ropeSegments[i];
    93.             RopeSegment secondSeg = this.ropeSegments[i + 1];
    94.  
    95.             Vector3 firstSegVelocity = firstSeg.posNow - firstSeg.posOld;
    96.             Vector3 secondSegVelocity = secondSeg.posNow - secondSeg.posOld;
    97.             float dist = (firstSeg.posNow - secondSeg.posNow).magnitude;
    98.             float error = Mathf.Abs(dist - this.ropeSegLen);
    99.  
    100.             Vector3 changeDir = Vector3.zero;
    101.  
    102.             if (dist > ropeSegLen)
    103.             {
    104.                 changeDir = (firstSeg.posNow - secondSeg.posNow).normalized;
    105.             }
    106.             else if (dist < ropeSegLen)
    107.             {
    108.                 changeDir = (secondSeg.posNow - firstSeg.posNow).normalized;
    109.             }
    110.  
    111.             Vector3 changeAmount = changeDir * error;
    112.             if (i != 0)
    113.             {
    114.                 firstSeg.posNow -= changeAmount * 0.5f;
    115.                 this.ropeSegments[i] = firstSeg;
    116.                 secondSeg.posNow += changeAmount * 0.5f;
    117.                 this.ropeSegments[i + 1] = secondSeg;
    118.  
    119.             }
    120.             else
    121.             {
    122.                 secondSeg.posNow += changeAmount;
    123.                 this.ropeSegments[i + 1] = secondSeg;
    124.             }
    125.         }
    126.     }
    127.  
    128.     public struct RopeSegment
    129.     {
    130.         public Vector3 posNow;
    131.         public Vector3 posOld;
    132.  
    133.         public Vector3 old_Vel;
    134.         public Vector3 new_Vel;
    135.         public Vector3 old_acc;
    136.         public Vector3 new_acc;
    137.  
    138.         public RopeSegment(Vector3 pos, Vector3 Velocity, Vector3 acc)
    139.         {
    140.             this.posNow = pos;
    141.             this.posOld = pos;
    142.             this.old_Vel = Velocity;
    143.             this.new_Vel = Velocity;
    144.             this.old_acc = acc;
    145.             this.new_acc = acc;
    146.         }
    147.     }
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,623
  3. jak6jak

    jak6jak

    Joined:
    Apr 17, 2013
    Posts:
    25
    Thanks for the response. The reason it looks similar is because it is from that repo. I changed it to velocity Verlet in hope that it would be improvement because I read somewhere that velocity Verlet has better energy conservation than regular Verlet. Unfortunately no such luck.
     
  4. jak6jak

    jak6jak

    Joined:
    Apr 17, 2013
    Posts:
    25
    Perhaps it might help to explain what I hope to be my end product. I am making a 3D game that involves swinging. I want the rope to be somewhat physically accurate so that it is eaier to implement complex behavior, such as the rope being attached to moving and/or spinning objects.

    I have looked at possibly implementing my rope as a simple pendulum but most tutorials online only teach pendulums that swing in a 2d motion and not with any initial velocity or reacting to outside forces.

    I have been in the process of researching a lot about physics for this project. I have learned some but have a ways to go before I consider myself anywhere near competent. This project could very well be more than I can chew, but given what progress I have already made I think I’ll be able to slowly iterate to an ideal solution.
     
  5. kdchabuk

    kdchabuk

    Joined:
    Feb 7, 2019
    Posts:
    48
    Could you also post a video of the rope's motion with no drag force? For example:
     
  6. jak6jak

    jak6jak

    Joined:
    Apr 17, 2013
    Posts:
    25
    Here is the rope without drag:

    And for fun here is the rope before I switched to Velocity verlet:


    As you can see the regular verlet works better but still loses a lot of its swinging ability.
     
  7. kdchabuk

    kdchabuk

    Joined:
    Feb 7, 2019
    Posts:
    48
    I believe the energy loss is due to the way the constraints are applied in ApplyConstraint(). posNew is directly adjusted without regard to energy; as you know, in Verlet, the velocity is computed from posNew and posOld, so by adjusting posNew, ApplyConstraint() is also adjusting the energy of the system. That repository has the same problem, but it is less noticeable.

    How to fix it would depend on your needs. For example, you could remove the constraints and instead add a spring force to each segment (see Hooke's_law).
     
  8. jak6jak

    jak6jak

    Joined:
    Apr 17, 2013
    Posts:
    25
    How might I change posNew without changing the energy of the system? Also can you elaborate on how a spring force might work?