Search Unity

Spring Simulation

Discussion in 'Scripting' started by suctioncup, May 12, 2014.

  1. suctioncup

    suctioncup

    Joined:
    May 19, 2012
    Posts:
    273
    So I am trying to model something similar to 'World of Goo's physics simulation. I have broken it down into simple spring forces, with each 'node' applying forces to bring it back to its original position.

    So I did some research, and the equation for a spring is:

    f = -kx - bv

    Where f is the force applied to the object, k is the 'stiffness' of the spring, x is the displacement, b is the 'dampening' value, and v is the velocity. (http://gafferongames.com/game-physics/spring-physics/)

    I implemented this as:

    Code (csharp):
    1. rigidbody.AddForce( (-k * (transform.position-anchor.transform.position-offset) - b * rigidbody.velocity) * a);
    and this works perfectly fine. However, it does not take into account the mass of the other nodes.

    If I attach multiple nodes in a line, apply gravity, and edit the mass of the end node - it droops lower, due to the gravity. However the other nodes do not change position at all - the end node should be dragging all the nodes down slightly.

    (A is just a multiplier)

    Help?

    Thanks in advance.
     
    Last edited: May 12, 2014
  2. suctioncup

    suctioncup

    Joined:
    May 19, 2012
    Posts:
    273
    Bump.

    Code (csharp):
    1. rigidbody.AddForce((-k * (transform.position-anchor.transform.position-offset- (attached.transform.position*e) ) - b * rigidbody.velocity) * a);// * (attached.transform.position/e));
    2.  
    I tried making it so every node takes into account the attached node, but it still doesnt work well. Hmm.
     
  3. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    A spring is pretty much nothing but force added in a direction in an attempt to reach a desired distance.

    force = Mathf.Abs(position - desired) / max * springForce;

    So in a rope situation you apply force on both sides to attempt to reach a common path. Secondarily, you then add the force of gravity.

    force = Physics.gravity;

    So in your "rope" all points in the rope receive gravity, and all but the last one receives the force form tension. only the last one would not recieve the second part of that force since there is no second unity to move towards.

    This is very similar to Unity's Cloth physics and Bullet's Soft Body Physics. Do research into those areas and you will probably get closer.
     
  4. suctioncup

    suctioncup

    Joined:
    May 19, 2012
    Posts:
    273
    I dont really understand how to implement this - I have the force of gravity applied via the rigidbody on each node. This provides the physics. Its the 'tension' I dont understand.
     
  5. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    each node is "pulling" on the ones its is attached to.

    edit:
    By the looks of it you've got the end node trying to "pull itself back" to its original position, but that act exerts a similar (but opposite) force on the node it is attached to. As it pulls itself back up, the other one is pulled down.
     
    Last edited: May 13, 2014
  6. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    lets see if I can explain it in a easy to understand method.

    f = -kx - bv

    f = the overall force
    k = the direction of the force
    x = the strength of the force
    b = the dampening effect amount (related to force)
    v = the velocity of the point

    OK, first, build two cubes and spread them apart. 1 cube gets a rigid body (call it link) the other is the base.

    Now, you want to make a script that is like this:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class TestBed : MonoBehaviour
    6. {
    7.     public Transform baseObj;
    8.  
    9.     void FixedUpdate (){
    10.         float strength = 100;
    11.  
    12.         float force = Vector3.Distance (baseObj, transform.position) * strength;
    13.         float direction = (baseObj.position - transform.position).normalized;
    14.  
    15.         if (baseObj.rigidbody) baseObj.rigidbody.AddForce (-force * direction);
    16.         if (rigidbody) rigidbody.AddForce (force * direction);
    17.     }
    18. }
    19.  
    OK, so the concept is, that you calculate the force based on the distance between the two objects. Force will naturally want to draw those objects together. Next you want to get the direction that force should be traveling.

    This then solves the first part. Direction and force. Next we work on the last part.

    Unity's Rigidbody provides an easy way to get the velocity. In this case, rigidbody.velocity.

    so now, we change the code above to simply add the velocity and dampening effect.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class TestBed : MonoBehaviour
    6. {
    7.     public Transform baseObj;
    8.  
    9.     void FixedUpdate (){
    10.         float strength = 100;
    11.         float damper = 1;
    12.  
    13.         float force = Vector3.Distance (baseObj, transform.position) * strength;
    14.         float direction = (baseObj.position - transform.position).normalized;
    15.         Vector3 velocity = rigidbody.velocity;
    16.  
    17.         if (baseObj.rigidbody) baseObj.rigidbody.AddForce (-force * direction - velocity * damper);
    18.         if (rigidbody) rigidbody.AddForce (force * direction + velocity * damper);
    19.     }
    20. }
    21.  
    Now, this solves the last part, velocity * damper. You will notice that the second force bonding is positive. (as opposed to the formula's negative) This is because we are adding force and dampening in the other direction, to force them together.

    This is all great if you have a bunch of balls together, but what if you had a rope of objects? Each object would want to draw it's self to the other objects, but if you used that code you would get a clump of things where you didn't want them. This is due to the fact that the above scripts use the transform.position and no offsets.

    For that, you will need to specify 2 points per link and use rigidbody.GetPointVelocity and rigidbody.AddForceAtPosition to add force and dampening to a body at another point.
     
  7. Tortuap

    Tortuap

    Joined:
    Dec 4, 2013
    Posts:
    137
    Nice explanation thank you.

    Your line
    Code (CSharp):
    1. float direction = (baseObj.position - transform.position).normalized;
    should be
    Code (CSharp):
    1. Vector3 direction = (transform.position - baseObj.position).normalized;
     
  8. joacoremis

    joacoremis

    Joined:
    May 7, 2019
    Posts:
    5
    I can confirm the avobe and I would add

    Code (CSharp):
    1.  
    2. if (baseObj.rigidbody) baseObj.rigidbody.AddForce (-force * direction - (velocity-baseObjRB.velocity) * damper);
    3. if (rigidbody) rigidbody.AddForce (force * direction + (velocity-baseObjRB.velocity) * damper);
    To take into account the other object's speed, I'm not 100% sure of that being the case, but it really helped with stability on high strength springs